@@ -430,10 +430,7 @@ final class PlatformSelectionBackend
430430 diagnosticCode: 'selection_lease_missing' ,
431431 );
432432 }
433- final beforeRead = await _platform.getClipboardRevision ();
434- final afterRead = await _platform.getClipboardRevision ();
435- if (beforeRead != lease.clipboardRevision ||
436- afterRead != lease.clipboardRevision ||
433+ if (! await _clipboardStillOwned (lease.clipboardRevision, replacement) ||
437434 ! await _matchesTarget (identity) ||
438435 await _platform.getForegroundWindow () != identity.handle) {
439436 throw const SelectionBackendException (
@@ -453,25 +450,58 @@ final class PlatformSelectionBackend
453450 return CommitVerification .unverified;
454451 }
455452
453+ static bool _sameClipboardText (String left, String right) {
454+ if (left == right) return true ;
455+ String normalize (String value) => value.replaceAll ('\r\n ' , '\n ' );
456+ return normalize (left) == normalize (right);
457+ }
458+
459+ Future <String ?> _readClipboardTextSafely () async {
460+ for (var attempt = 0 ; attempt < 2 ; attempt++ ) {
461+ try {
462+ return await _clipboard.readText ();
463+ } catch (_) {
464+ if (attempt == 0 ) {
465+ await Future <void >.delayed (const Duration (milliseconds: 30 ));
466+ }
467+ }
468+ }
469+ return null ;
470+ }
471+
472+ Future <bool > _clipboardStillOwned (int revision, String expectedText) async {
473+ final currentRevision = await _platform.getClipboardRevision ();
474+ if (currentRevision == revision) return true ;
475+ final currentText = await _readClipboardTextSafely ();
476+ if (currentText == null ||
477+ ! _sameClipboardText (currentText, expectedText) ||
478+ await _platform.getClipboardRevision () != currentRevision) {
479+ return false ;
480+ }
481+ _ownedClipboardText[currentRevision] = expectedText;
482+ return true ;
483+ }
484+
456485 @override
457486 Future <ClipboardRestoreResult > restoreClipboard (
458487 ClipboardSnapshot snapshot, {
459488 required int expectedRevision,
460489 bool restoreOriginal = true ,
461490 }) async {
462491 try {
463- final currentRevision = await _platform.getClipboardRevision ();
464492 final expectedText = _ownedClipboardText[expectedRevision];
465- if (currentRevision != expectedRevision || expectedText == null ) {
493+ if (expectedText == null ||
494+ ! await _clipboardStillOwned (expectedRevision, expectedText)) {
466495 return ClipboardRestoreResult .skippedExternalChange;
467496 }
497+ final currentRevision = await _platform.getClipboardRevision ();
468498 final supportsAtomicTransactions =
469499 _platform.supportsAtomicTextClipboardTransactions ();
470500 if (! supportsAtomicTransactions) {
471501 final currentText = await _clipboard.readText ();
472502 final revisionAfterRead = await _platform.getClipboardRevision ();
473- if (revisionAfterRead != expectedRevision ||
474- currentText != expectedText) {
503+ if (revisionAfterRead != currentRevision ||
504+ ! _sameClipboardText ( currentText, expectedText) ) {
475505 return ClipboardRestoreResult .skippedExternalChange;
476506 }
477507 }
@@ -488,7 +518,7 @@ final class PlatformSelectionBackend
488518 final originalData = original! ;
489519 final usesNativeSnapshot = originalData is PlatformClipboardSnapshot ;
490520 if (supportsAtomicTransactions || usesNativeSnapshot) {
491- var recoveryRevision = expectedRevision ;
521+ var recoveryRevision = currentRevision ;
492522 var recoveryText = expectedText;
493523 for (var attempt = 0 ; attempt < 3 ; attempt++ ) {
494524 try {
@@ -559,7 +589,7 @@ final class PlatformSelectionBackend
559589 }
560590 }
561591 } else {
562- if (await _platform.getClipboardRevision () != expectedRevision ) {
592+ if (await _platform.getClipboardRevision () != currentRevision ) {
563593 return ClipboardRestoreResult .skippedExternalChange;
564594 }
565595 if (originalData is ! String ) {
0 commit comments