1120988e
fix: revoke expired selection replies
a73x 2026-08-18 12:27
Commit message
web/mux.js
| Old | New | ||
|---|---|---|---|
| @@ -149,6 +149,7 @@ class Tile { | |||
| 149 | this.nextSelectionId = 0; | 149 | this.nextSelectionId = 0; |
| 150 | this.selectionRequestTimer = null; | 150 | this.selectionRequestTimer = null; |
| 151 | this.selectionRequestVersion = 0; | 151 | this.selectionRequestVersion = 0; |
| 152 | this.activeSelectionRequest = null; // {selection, id, generation} | ||
| 152 | this.selectionFeedbackTimer = null; | 153 | this.selectionFeedbackTimer = null; |
| 153 | this.selectionFeedbackVersion = 0; | 154 | this.selectionFeedbackVersion = 0; |
| 154 | this.drag = null; | 155 | this.drag = null; |
| @@ -672,9 +673,12 @@ class Tile { | |||
| 672 | } | 673 | } |
| 673 | 674 | ||
| 674 | onSelectionReply() { | 675 | onSelectionReply() { |
| 675 | if (!this.selection) return; | 676 | const active = this.activeSelectionRequest; |
| 677 | if (!this.selection || !active || active.selection !== this.selection || | ||
| 678 | active.id !== this.selection.requestId || | ||
| 679 | active.generation !== this.selectionRequestVersion) return; | ||
| 676 | // A Zig u32 result reaches JavaScript as a signed WASM i32. | 680 | // A Zig u32 result reaches JavaScript as a signed WASM i32. |
| 677 | if ((this.core.mux_selection_id() >>> 0) !== this.selection.requestId) return; | 681 | if ((this.core.mux_selection_id() >>> 0) !== active.id) return; |
| 678 | this.invalidateSelectionRequest(); | 682 | this.invalidateSelectionRequest(); |
| 679 | if (this.core.mux_selection_status() !== 0) { | 683 | if (this.core.mux_selection_status() !== 0) { |
| 680 | this.selection.text = null; | 684 | this.selection.text = null; |
| @@ -728,22 +732,29 @@ class Tile { | |||
| 728 | invalidateSelectionRequest() { | 732 | invalidateSelectionRequest() { |
| 729 | if (this.selectionRequestTimer !== null) clearTimeout(this.selectionRequestTimer); | 733 | if (this.selectionRequestTimer !== null) clearTimeout(this.selectionRequestTimer); |
| 730 | this.selectionRequestTimer = null; | 734 | this.selectionRequestTimer = null; |
| 735 | this.activeSelectionRequest = null; | ||
| 731 | this.selectionRequestVersion++; | 736 | this.selectionRequestVersion++; |
| 732 | } | 737 | } |
| 733 | 738 | ||
| 734 | startSelectionRequestTimeout(selection, reservedVersion = null) { | 739 | activateSelectionRequest(selection) { |
| 735 | if (reservedVersion === null) { | 740 | this.invalidateSelectionRequest(); |
| 736 | this.invalidateSelectionRequest(); | 741 | const request = { |
| 737 | reservedVersion = this.selectionRequestVersion; | 742 | selection, |
| 738 | } | 743 | id: selection.requestId, |
| 739 | const version = reservedVersion; | 744 | generation: this.selectionRequestVersion, |
| 740 | const requestId = selection.requestId; | 745 | }; |
| 746 | this.activeSelectionRequest = request; | ||
| 747 | return request; | ||
| 748 | } | ||
| 749 | |||
| 750 | startSelectionRequestTimeout(request) { | ||
| 751 | const selection = request.selection; | ||
| 741 | const timer = setTimeout(() => { | 752 | const timer = setTimeout(() => { |
| 742 | if (this.selectionRequestTimer !== timer || | 753 | if (this.selectionRequestTimer !== timer || |
| 743 | this.selectionRequestVersion !== version || | 754 | this.activeSelectionRequest !== request || |
| 744 | this.selection !== selection || selection.requestId !== requestId) return; | 755 | this.selectionRequestVersion !== request.generation || |
| 745 | this.selectionRequestTimer = null; | 756 | this.selection !== selection || selection.requestId !== request.id) return; |
| 746 | this.selectionRequestVersion++; | 757 | this.invalidateSelectionRequest(); |
| 747 | selection.text = null; | 758 | selection.text = null; |
| 748 | this.showSelectionUnavailable(); | 759 | this.showSelectionUnavailable(); |
| 749 | }, SELECTION_REQUEST_TIMEOUT_MS); | 760 | }, SELECTION_REQUEST_TIMEOUT_MS); |
| @@ -835,16 +846,16 @@ class Tile { | |||
| 835 | ); | 846 | ); |
| 836 | if (n === 16) { | 847 | if (n === 16) { |
| 837 | const selection = this.selection; | 848 | const selection = this.selection; |
| 838 | this.invalidateSelectionRequest(); | 849 | // Establish reply authority before send: a host is allowed to deliver |
| 839 | const requestVersion = this.selectionRequestVersion; | 850 | // a matching semantic reply synchronously from its send hook. |
| 851 | const request = this.activateSelectionRequest(selection); | ||
| 840 | if (!this.sendFrame(MSG.selection_req, this.outBytes())) { | 852 | if (!this.sendFrame(MSG.selection_req, this.outBytes())) { |
| 841 | this.clearSelection(); | 853 | this.clearSelection(); |
| 842 | return; | 854 | return; |
| 843 | } | 855 | } |
| 844 | if (endpointChanged) this.reflow(); | 856 | if (endpointChanged) this.reflow(); |
| 845 | if (this.selection === selection && | 857 | if (this.activeSelectionRequest === request) |
| 846 | this.selectionRequestVersion === requestVersion) | 858 | this.startSelectionRequestTimeout(request); |
| 847 | this.startSelectionRequestTimeout(selection, requestVersion); | ||
| 848 | } | 859 | } |
| 849 | else this.clearSelection(); | 860 | else this.clearSelection(); |
| 850 | } | 861 | } |
web/verify.js
| Old | New | ||
|---|---|---|---|
| @@ -730,6 +730,7 @@ async function verifySelectionShell(shell, html) { | |||
| 730 | && tile.scrollRequestTimer === null | 730 | && tile.scrollRequestTimer === null |
| 731 | && tile.selectionRequestTimer === null | 731 | && tile.selectionRequestTimer === null |
| 732 | && tile.selectionRequestVersion === 0 | 732 | && tile.selectionRequestVersion === 0 |
| 733 | && tile.activeSelectionRequest === null | ||
| 733 | && tile.selectionFeedbackTimer === null | 734 | && tile.selectionFeedbackTimer === null |
| 734 | && tile.selectionFeedbackVersion === 0 | 735 | && tile.selectionFeedbackVersion === 0 |
| 735 | && tile.selectionCopyVersion === 0; | 736 | && tile.selectionCopyVersion === 0; |
| @@ -859,6 +860,8 @@ async function verifySelectionShell(shell, html) { | |||
| 859 | envelope.set(payload, 6); | 860 | envelope.set(payload, 6); |
| 860 | return envelope; | 861 | return envelope; |
| 861 | }; | 862 | }; |
| 863 | const authorizeSelectionReply = (fixture) => | ||
| 864 | fixture.tile.activateSelectionRequest(fixture.tile.selection); | ||
| 862 | 865 | ||
| 863 | const transport = makeTile(); | 866 | const transport = makeTile(); |
| 864 | transport.tile.sendFrame = h.Tile.prototype.sendFrame.bind(transport.tile); | 867 | transport.tile.sendFrame = h.Tile.prototype.sendFrame.bind(transport.tile); |
| @@ -997,7 +1000,9 @@ async function verifySelectionShell(shell, html) { | |||
| 997 | snapshotHistory.tile.selection = { | 1000 | snapshotHistory.tile.selection = { |
| 998 | anchor: { row: 21, col: 1 }, active: { row: 24, col: 3 }, requestId: 90, text: 'old grid', | 1001 | anchor: { row: 21, col: 1 }, active: { row: 24, col: 3 }, requestId: 90, text: 'old grid', |
| 999 | }; | 1002 | }; |
| 1000 | snapshotHistory.tile.startSelectionRequestTimeout(snapshotHistory.tile.selection); | 1003 | snapshotHistory.tile.startSelectionRequestTimeout( |
| 1004 | snapshotHistory.tile.activateSelectionRequest(snapshotHistory.tile.selection), | ||
| 1005 | ); | ||
| 1001 | const snapshotSelectionHandle = snapshotHistory.tile.selectionRequestTimer; | 1006 | const snapshotSelectionHandle = snapshotHistory.tile.selectionRequestTimer; |
| 1002 | const snapshotSelectionTimer = snapshotSelectionHandle === null | 1007 | const snapshotSelectionTimer = snapshotSelectionHandle === null |
| 1003 | ? undefined : h.timers[snapshotSelectionHandle - 1]; | 1008 | ? undefined : h.timers[snapshotSelectionHandle - 1]; |
| @@ -1431,6 +1436,31 @@ async function verifySelectionShell(shell, html) { | |||
| 1431 | check('selection response timeout clears timer identity', selectionTimeout.tile.selectionRequestTimer, null); | 1436 | check('selection response timeout clears timer identity', selectionTimeout.tile.selectionRequestTimer, null); |
| 1432 | check('selection response timeout retains no authoritative text', selectionTimeout.tile.selection?.text, null); | 1437 | check('selection response timeout retains no authoritative text', selectionTimeout.tile.selection?.text, null); |
| 1433 | check('selection response timeout reports transient unavailability', `${selectionTimeout.tile.copyButton.className}|${selectionTimeout.tile.copyButton.textContent}`, 'copy-request on error|Selection unavailable'); | 1438 | check('selection response timeout reports transient unavailability', `${selectionTimeout.tile.copyButton.className}|${selectionTimeout.tile.copyButton.textContent}`, 'copy-request on error|Selection unavailable'); |
| 1439 | check('selection response timeout revokes active request capability', selectionTimeout.tile.activeSelectionRequest, null); | ||
| 1440 | |||
| 1441 | const lateAfterTimeout = makeTile(); | ||
| 1442 | lateAfterTimeout.tile.pendingClipboard = 'OSC fallback after timeout'; | ||
| 1443 | lateAfterTimeout.tile.clipboardVersion = 1; | ||
| 1444 | lateAfterTimeout.tile.copyUiOwner = 'clipboard'; | ||
| 1445 | lateAfterTimeout.tile.canvas.dispatchEvent('pointerdown', pointer(41, 1, 1)); | ||
| 1446 | lateAfterTimeout.tile.canvas.dispatchEvent('pointerup', pointer(41, 4, 2)); | ||
| 1447 | const lateLeaseHandle = lateAfterTimeout.tile.selectionRequestTimer; | ||
| 1448 | const lateLeaseTimer = lateLeaseHandle === null | ||
| 1449 | ? undefined : h.timers[lateLeaseHandle - 1]; | ||
| 1450 | lateLeaseTimer?.fn(); | ||
| 1451 | const lateFeedbackHandle = lateAfterTimeout.tile.selectionFeedbackTimer; | ||
| 1452 | const lateFeedbackTimer = lateFeedbackHandle === null | ||
| 1453 | ? undefined : h.timers[lateFeedbackHandle - 1]; | ||
| 1454 | lateFeedbackTimer?.fn(); | ||
| 1455 | check('expired selection feedback restores OSC fallback before late reply', lateAfterTimeout.tile.copyUiOwner, 'clipboard'); | ||
| 1456 | lateAfterTimeout.setResult(1, 0, Buffer.from('must stay expired')); | ||
| 1457 | lateAfterTimeout.tile.onSelectionReply(); | ||
| 1458 | check('late same-id success cannot restore expired authoritative text', lateAfterTimeout.tile.selection?.text, null); | ||
| 1459 | check('late same-id success cannot reclaim shared copy UI', lateAfterTimeout.tile.copyUiOwner, 'clipboard'); | ||
| 1460 | lateAfterTimeout.setResult(1, 2, []); | ||
| 1461 | lateAfterTimeout.tile.onSelectionReply(); | ||
| 1462 | check('late same-id failure cannot reclaim shared copy UI', lateAfterTimeout.tile.copyUiOwner, 'clipboard'); | ||
| 1463 | check('late same-id failure cannot start new unavailable feedback', lateAfterTimeout.tile.selectionFeedbackTimer, null); | ||
| 1434 | 1464 | ||
| 1435 | const replyBeforeTimeout = makeTile(); | 1465 | const replyBeforeTimeout = makeTile(); |
| 1436 | replyBeforeTimeout.tile.canvas.dispatchEvent('pointerdown', pointer(33, 1, 1)); | 1466 | replyBeforeTimeout.tile.canvas.dispatchEvent('pointerdown', pointer(33, 1, 1)); |
| @@ -1443,6 +1473,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1443 | check('matching selection reply clears response timeout', replyTimeoutTask?.cleared, true); | 1473 | check('matching selection reply clears response timeout', replyTimeoutTask?.cleared, true); |
| 1444 | check('matching selection reply clears timeout identity', replyBeforeTimeout.tile.selectionRequestTimer, null); | 1474 | check('matching selection reply clears timeout identity', replyBeforeTimeout.tile.selectionRequestTimer, null); |
| 1445 | check('matching selection reply retains authoritative text', replyBeforeTimeout.tile.selection?.text, 'reply before timeout'); | 1475 | check('matching selection reply retains authoritative text', replyBeforeTimeout.tile.selection?.text, 'reply before timeout'); |
| 1476 | check('matching selection reply consumes active request capability', replyBeforeTimeout.tile.activeSelectionRequest, null); | ||
| 1446 | 1477 | ||
| 1447 | const synchronousReply = makeTile(); | 1478 | const synchronousReply = makeTile(); |
| 1448 | synchronousReply.tile.sendFrame = () => { | 1479 | synchronousReply.tile.sendFrame = () => { |
| @@ -1454,6 +1485,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1454 | synchronousReply.tile.canvas.dispatchEvent('pointerup', pointer(40, 4, 2)); | 1485 | synchronousReply.tile.canvas.dispatchEvent('pointerup', pointer(40, 4, 2)); |
| 1455 | check('synchronous host reply retains authoritative selection', synchronousReply.tile.selection?.text, 'synchronous host reply'); | 1486 | check('synchronous host reply retains authoritative selection', synchronousReply.tile.selection?.text, 'synchronous host reply'); |
| 1456 | check('synchronous host reply cannot acquire a stale response timeout', synchronousReply.tile.selectionRequestTimer, null); | 1487 | check('synchronous host reply cannot acquire a stale response timeout', synchronousReply.tile.selectionRequestTimer, null); |
| 1488 | check('synchronous host reply consumes pre-send active capability', synchronousReply.tile.activeSelectionRequest, null); | ||
| 1457 | 1489 | ||
| 1458 | const failureBeforeTimeout = makeTile(); | 1490 | const failureBeforeTimeout = makeTile(); |
| 1459 | failureBeforeTimeout.tile.canvas.dispatchEvent('pointerdown', pointer(34, 1, 1)); | 1491 | failureBeforeTimeout.tile.canvas.dispatchEvent('pointerdown', pointer(34, 1, 1)); |
| @@ -1481,6 +1513,23 @@ async function verifySelectionShell(shell, html) { | |||
| 1481 | check('stale selection timeout cannot clear newer response timer', staleSelectionTimeout.tile.selectionRequestTimer, newerSelectionTimer); | 1513 | check('stale selection timeout cannot clear newer response timer', staleSelectionTimeout.tile.selectionRequestTimer, newerSelectionTimer); |
| 1482 | check('stale selection timeout cannot show unavailable for newer request', staleSelectionTimeout.tile.copyUiOwner, null); | 1514 | check('stale selection timeout cannot show unavailable for newer request', staleSelectionTimeout.tile.copyUiOwner, null); |
| 1483 | 1515 | ||
| 1516 | const staleSelectionReply = makeTile(); | ||
| 1517 | staleSelectionReply.tile.canvas.dispatchEvent('pointerdown', pointer(42, 1, 1)); | ||
| 1518 | staleSelectionReply.tile.canvas.dispatchEvent('pointerup', pointer(42, 4, 2)); | ||
| 1519 | staleSelectionReply.tile.canvas.dispatchEvent('pointerdown', pointer(43, 2, 2)); | ||
| 1520 | staleSelectionReply.tile.canvas.dispatchEvent('pointerup', pointer(43, 5, 3)); | ||
| 1521 | const activeNewerRequest = staleSelectionReply.tile.activeSelectionRequest; | ||
| 1522 | check('newer selection establishes active pre-send capability', activeNewerRequest !== null, true); | ||
| 1523 | check('newer selection active capability carries request id', activeNewerRequest?.id, 2); | ||
| 1524 | staleSelectionReply.setResult(1, 0, Buffer.from('stale older reply')); | ||
| 1525 | staleSelectionReply.tile.onSelectionReply(); | ||
| 1526 | check('older reply cannot consume newer active request capability', staleSelectionReply.tile.activeSelectionRequest, activeNewerRequest); | ||
| 1527 | check('older reply cannot bind text to newer selection', staleSelectionReply.tile.selection?.text, null); | ||
| 1528 | staleSelectionReply.setResult(2, 0, Buffer.from('matching newer reply')); | ||
| 1529 | staleSelectionReply.tile.onSelectionReply(); | ||
| 1530 | check('matching newer reply binds after stale older reply', staleSelectionReply.tile.selection?.text, 'matching newer reply'); | ||
| 1531 | check('matching newer reply consumes active capability', staleSelectionReply.tile.activeSelectionRequest, null); | ||
| 1532 | |||
| 1484 | const selectionLifecycle = makeTile(); | 1533 | const selectionLifecycle = makeTile(); |
| 1485 | selectionLifecycle.tile.canvas.dispatchEvent('pointerdown', pointer(37, 1, 1)); | 1534 | selectionLifecycle.tile.canvas.dispatchEvent('pointerdown', pointer(37, 1, 1)); |
| 1486 | selectionLifecycle.tile.canvas.dispatchEvent('pointerup', pointer(37, 4, 2)); | 1535 | selectionLifecycle.tile.canvas.dispatchEvent('pointerup', pointer(37, 4, 2)); |
| @@ -1585,6 +1634,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1585 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: highId, text: null, | 1634 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: highId, text: null, |
| 1586 | }; | 1635 | }; |
| 1587 | const authoritative = Buffer.from('daemon ☃', 'utf8'); | 1636 | const authoritative = Buffer.from('daemon ☃', 'utf8'); |
| 1637 | authorizeSelectionReply(replies); | ||
| 1588 | replies.setResult(highId, 0, authoritative); | 1638 | replies.setResult(highId, 0, authoritative); |
| 1589 | replies.tile.onSelectionReply(); | 1639 | replies.tile.onSelectionReply(); |
| 1590 | new Uint8Array(replies.memory.buffer).fill(0, 96, 96 + authoritative.length); | 1640 | new Uint8Array(replies.memory.buffer).fill(0, 96, 96 + authoritative.length); |
| @@ -1595,12 +1645,14 @@ async function verifySelectionShell(shell, html) { | |||
| 1595 | replies.tile.onSelectionReply(); | 1645 | replies.tile.onSelectionReply(); |
| 1596 | check('stale semantic reply cannot replace retained text', replies.tile.selection.text, 'keep'); | 1646 | check('stale semantic reply cannot replace retained text', replies.tile.selection.text, 'keep'); |
| 1597 | 1647 | ||
| 1648 | authorizeSelectionReply(replies); | ||
| 1598 | replies.setResult(highId, 2, []); | 1649 | replies.setResult(highId, 2, []); |
| 1599 | replies.tile.onSelectionReply(); | 1650 | replies.tile.onSelectionReply(); |
| 1600 | check('failed matching reply clears authoritative text', replies.tile.selection.text, null); | 1651 | check('failed matching reply clears authoritative text', replies.tile.selection.text, null); |
| 1601 | check('failed matching reply reports selection unavailable', `${replies.tile.copyButton.className}|${replies.tile.copyButton.textContent}`, 'copy-request on error|Selection unavailable'); | 1652 | check('failed matching reply reports selection unavailable', `${replies.tile.copyButton.className}|${replies.tile.copyButton.textContent}`, 'copy-request on error|Selection unavailable'); |
| 1602 | 1653 | ||
| 1603 | replies.tile.selection.text = 'previous'; | 1654 | replies.tile.selection.text = 'previous'; |
| 1655 | authorizeSelectionReply(replies); | ||
| 1604 | replies.setResult(highId, 0, [0xff]); | 1656 | replies.setResult(highId, 0, [0xff]); |
| 1605 | replies.tile.onSelectionReply(); | 1657 | replies.tile.onSelectionReply(); |
| 1606 | check('browser rejects invalid UTF-8 selection text defensively', replies.tile.selection.text, null); | 1658 | check('browser rejects invalid UTF-8 selection text defensively', replies.tile.selection.text, null); |
| @@ -1615,6 +1667,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1615 | routed.tile.selection = { | 1667 | routed.tile.selection = { |
| 1616 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 77, text: null, | 1668 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 77, text: null, |
| 1617 | }; | 1669 | }; |
| 1670 | authorizeSelectionReply(routed); | ||
| 1618 | routed.setResult(77, 0, Buffer.from('routed')); | 1671 | routed.setResult(77, 0, Buffer.from('routed')); |
| 1619 | const replyPayload = Uint8Array.from([77, 0, 0, 0, 0, ...Buffer.from('routed')]); | 1672 | const replyPayload = Uint8Array.from([77, 0, 0, 0, 0, ...Buffer.from('routed')]); |
| 1620 | const replyEnvelope = new Uint8Array(6 + replyPayload.length); | 1673 | const replyEnvelope = new Uint8Array(6 + replyPayload.length); |
| @@ -1632,6 +1685,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1632 | blockedCopy.tile.selection = { | 1685 | blockedCopy.tile.selection = { |
| 1633 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 71, text: null, | 1686 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 71, text: null, |
| 1634 | }; | 1687 | }; |
| 1688 | authorizeSelectionReply(blockedCopy); | ||
| 1635 | blockedCopy.setResult(71, 3, []); | 1689 | blockedCopy.setResult(71, 3, []); |
| 1636 | blockedCopy.tile.onSelectionReply(); | 1690 | blockedCopy.tile.onSelectionReply(); |
| 1637 | const unavailableFeedbackHandle = blockedCopy.tile.selectionFeedbackTimer; | 1691 | const unavailableFeedbackHandle = blockedCopy.tile.selectionFeedbackTimer; |
| @@ -1653,6 +1707,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1653 | ownerChangedFeedback.tile.selection = { | 1707 | ownerChangedFeedback.tile.selection = { |
| 1654 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 78, text: null, | 1708 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 78, text: null, |
| 1655 | }; | 1709 | }; |
| 1710 | authorizeSelectionReply(ownerChangedFeedback); | ||
| 1656 | ownerChangedFeedback.setResult(78, 3, []); | 1711 | ownerChangedFeedback.setResult(78, 3, []); |
| 1657 | ownerChangedFeedback.tile.onSelectionReply(); | 1712 | ownerChangedFeedback.tile.onSelectionReply(); |
| 1658 | const supersededFeedbackHandle = ownerChangedFeedback.tile.selectionFeedbackTimer; | 1713 | const supersededFeedbackHandle = ownerChangedFeedback.tile.selectionFeedbackTimer; |
| @@ -1673,6 +1728,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1673 | copySupersedesFeedback.tile.selection = { | 1728 | copySupersedesFeedback.tile.selection = { |
| 1674 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 79, text: null, | 1729 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 79, text: null, |
| 1675 | }; | 1730 | }; |
| 1731 | authorizeSelectionReply(copySupersedesFeedback); | ||
| 1676 | copySupersedesFeedback.setResult(79, 3, []); | 1732 | copySupersedesFeedback.setResult(79, 3, []); |
| 1677 | copySupersedesFeedback.tile.onSelectionReply(); | 1733 | copySupersedesFeedback.tile.onSelectionReply(); |
| 1678 | const preCopyFeedbackHandle = copySupersedesFeedback.tile.selectionFeedbackTimer; | 1734 | const preCopyFeedbackHandle = copySupersedesFeedback.tile.selectionFeedbackTimer; |
| @@ -1689,6 +1745,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1689 | unzoomFeedback.tile.selection = { | 1745 | unzoomFeedback.tile.selection = { |
| 1690 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 81, text: null, | 1746 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 81, text: null, |
| 1691 | }; | 1747 | }; |
| 1748 | authorizeSelectionReply(unzoomFeedback); | ||
| 1692 | unzoomFeedback.setResult(81, 3, []); | 1749 | unzoomFeedback.setResult(81, 3, []); |
| 1693 | unzoomFeedback.tile.onSelectionReply(); | 1750 | unzoomFeedback.tile.onSelectionReply(); |
| 1694 | const unzoomFeedbackHandle = unzoomFeedback.tile.selectionFeedbackTimer; | 1751 | const unzoomFeedbackHandle = unzoomFeedback.tile.selectionFeedbackTimer; |
| @@ -1710,6 +1767,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1710 | inFlightUi.tile.selection = { | 1767 | inFlightUi.tile.selection = { |
| 1711 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 72, text: null, | 1768 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 72, text: null, |
| 1712 | }; | 1769 | }; |
| 1770 | authorizeSelectionReply(inFlightUi); | ||
| 1713 | inFlightUi.setResult(72, 2, []); | 1771 | inFlightUi.setResult(72, 2, []); |
| 1714 | inFlightUi.tile.onSelectionReply(); | 1772 | inFlightUi.tile.onSelectionReply(); |
| 1715 | inFlightWrite.resolve(); | 1773 | inFlightWrite.resolve(); |
| @@ -1730,6 +1788,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1730 | timerUi.tile.selection = { | 1788 | timerUi.tile.selection = { |
| 1731 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 73, text: null, | 1789 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 73, text: null, |
| 1732 | }; | 1790 | }; |
| 1791 | authorizeSelectionReply(timerUi); | ||
| 1733 | timerUi.setResult(73, 1, []); | 1792 | timerUi.setResult(73, 1, []); |
| 1734 | timerUi.tile.onSelectionReply(); | 1793 | timerUi.tile.onSelectionReply(); |
| 1735 | oldClipboardTimer.fn(); | 1794 | oldClipboardTimer.fn(); |
| @@ -1743,6 +1802,7 @@ async function verifySelectionShell(shell, html) { | |||
| 1743 | clearedUi.tile.selection = { | 1802 | clearedUi.tile.selection = { |
| 1744 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 74, text: null, | 1803 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 74, text: null, |
| 1745 | }; | 1804 | }; |
| 1805 | authorizeSelectionReply(clearedUi); | ||
| 1746 | clearedUi.setResult(74, 3, []); | 1806 | clearedUi.setResult(74, 3, []); |
| 1747 | clearedUi.tile.onSelectionReply(); | 1807 | clearedUi.tile.onSelectionReply(); |
| 1748 | clearedUi.tile.clearSelection(false); | 1808 | clearedUi.tile.clearSelection(false); |
| @@ -1752,8 +1812,10 @@ async function verifySelectionShell(shell, html) { | |||
| 1752 | successfulUi.tile.selection = { | 1812 | successfulUi.tile.selection = { |
| 1753 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 75, text: null, | 1813 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 75, text: null, |
| 1754 | }; | 1814 | }; |
| 1815 | authorizeSelectionReply(successfulUi); | ||
| 1755 | successfulUi.setResult(75, 3, []); | 1816 | successfulUi.setResult(75, 3, []); |
| 1756 | successfulUi.tile.onSelectionReply(); | 1817 | successfulUi.tile.onSelectionReply(); |
| 1818 | authorizeSelectionReply(successfulUi); | ||
| 1757 | successfulUi.setResult(75, 0, Buffer.from('available')); | 1819 | successfulUi.setResult(75, 0, Buffer.from('available')); |
| 1758 | successfulUi.tile.onSelectionReply(); | 1820 | successfulUi.tile.onSelectionReply(); |
| 1759 | check('successful selection removes stale unavailable UI', `${successfulUi.tile.copyButton.className}|${successfulUi.tile.copyButton.textContent}`, 'copy-request|Copy'); | 1821 | check('successful selection removes stale unavailable UI', `${successfulUi.tile.copyButton.className}|${successfulUi.tile.copyButton.textContent}`, 'copy-request|Copy'); |
| @@ -2061,6 +2123,7 @@ async function verifySelectionShell(shell, html) { | |||
| 2061 | newSelectionUi.tile.selection = { | 2123 | newSelectionUi.tile.selection = { |
| 2062 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 76, text: null, | 2124 | anchor: { row: 0, col: 0 }, active: { row: 0, col: 1 }, requestId: 76, text: null, |
| 2063 | }; | 2125 | }; |
| 2126 | authorizeSelectionReply(newSelectionUi); | ||
| 2064 | newSelectionUi.setResult(76, 3, []); | 2127 | newSelectionUi.setResult(76, 3, []); |
| 2065 | newSelectionUi.tile.onSelectionReply(); | 2128 | newSelectionUi.tile.onSelectionReply(); |
| 2066 | const oldUnavailableHandle = newSelectionUi.tile.selectionFeedbackTimer; | 2129 | const oldUnavailableHandle = newSelectionUi.tile.selectionFeedbackTimer; |