|
4 | 4 | using System.Runtime.InteropServices; |
5 | 5 | using System.Windows.Automation; |
6 | 6 | using System.Windows.Input; |
7 | | -using ThrottleDebounce; |
8 | 7 | using Unfucked; |
9 | 8 |
|
10 | 9 | namespace AuthenticatorChooser; |
@@ -46,31 +45,29 @@ public override void chooseUsbSecurityKey(SystemWindow fidoPrompt) { |
46 | 45 | } |
47 | 46 |
|
48 | 47 | IEnumerable<string> expectedTitles = I18N.getStrings(I18N.Key.SIGN_IN_WITH_YOUR_PASSKEY).Concat(skipAllNonSecurityKeyOptions ? I18N.getStrings(I18N.Key.MAKING_SURE_ITS_YOU) : []).ToList(); |
49 | | - try { |
50 | | - // #21: title not rendered immediately |
51 | | - AutomationElement titleLabel = retry(() => outerScrollViewer.FindFirst(TreeScope.Children, TITLE_CONDITION) ?? throw new NullReferenceException(), 18); // #4, #15 |
52 | | - string? actualTitle = titleLabel.GetCurrentPropertyValue(AutomationElement.NameProperty) as string; |
| 48 | + // #21: title not rendered immediately |
| 49 | + if (outerScrollViewer.WaitForFirst(TreeScope.Children, TITLE_CONDITION, TimeSpan.FromSeconds(5)) is { } titleLabel) { |
| 50 | + string? actualTitle = titleLabel.GetCurrentPropertyValue(AutomationElement.NameProperty) as string; |
53 | 51 | if (expectedTitles.Any(expected => expected.Equals(actualTitle, StringComparison.CurrentCulture))) { |
54 | 52 | LOGGER.Trace("Found dialog title {0:N3} sec after dialog opened", overallStopwatch.Elapsed.TotalSeconds); |
55 | 53 | } else { |
56 | 54 | LOGGER.Debug("Window is not a passkey choice prompt because the first TextBlock child of the ScrollViewer has the name {actual} instead of {expected}", actualTitle, |
57 | 55 | string.Join(" or ", expectedTitles)); |
58 | 56 | return; |
59 | 57 | } |
60 | | - } catch (NullReferenceException) { |
| 58 | + } else { |
61 | 59 | LOGGER.Debug("Window is not a passkey choice prompt because there is no TextBlock child of the ScrollViewer after retrying for {0:N3}", overallStopwatch.Elapsed.TotalSeconds); |
62 | 60 | return; |
63 | 61 | } |
64 | 62 |
|
65 | 63 | LOGGER.Trace("Window 0x{hwnd:x} is a Windows Security window", fidoPrompt.HWnd); |
66 | 64 |
|
67 | | - Stopwatch authenticatorChoicesStopwatch = Stopwatch.StartNew(); |
68 | | - ICollection<AutomationElement> authenticatorChoices; |
69 | | - try { |
70 | | - authenticatorChoices = retry(() => outerScrollViewer.FindFirst(TreeScope.Children, CREDENTIALS_LIST_ID_CONDITION).Children().ToList(), 127); |
| 65 | + Stopwatch authenticatorChoicesStopwatch = Stopwatch.StartNew(); |
| 66 | + // #5, #11: power series backoff, max=500 ms per attempt, ~1 minute total |
| 67 | + if (outerScrollViewer.WaitForFirst(TreeScope.Children, CREDENTIALS_LIST_ID_CONDITION, el => el.Children().ToList(), TimeSpan.FromMinutes(1)) is { } authenticatorChoices) { |
71 | 68 | LOGGER.Trace("Found authenticator choices after retrying for {0:N3} sec", authenticatorChoicesStopwatch.Elapsed.TotalSeconds); |
72 | | - } catch (Exception e) when (e is not OutOfMemoryException) { |
73 | | - LOGGER.Warn(e, "Could not find authenticator choices after retrying for {0:N3} sec due to the following exception. Giving up and not automatically selecting Security Key.", |
| 69 | + } else { |
| 70 | + LOGGER.Warn("Could not find authenticator choices after retrying for {0:N3} sec due to the following exception. Giving up and not automatically selecting Security Key.", |
74 | 71 | authenticatorChoicesStopwatch.Elapsed.TotalSeconds); |
75 | 72 | return; |
76 | 73 | } |
@@ -113,17 +110,15 @@ public override void chooseUsbSecurityKey(SystemWindow fidoPrompt) { |
113 | 110 | ((InvokePattern) nextButton.GetCurrentPattern(InvokePattern.Pattern)).Invoke(); |
114 | 111 | LOGGER.Info("Next button pressed {0:N3} sec after dialog appeared", overallStopwatch.Elapsed.TotalSeconds); |
115 | 112 | } |
| 113 | + } catch (ElementNotAvailableException e) { |
| 114 | + LOGGER.Error(e, "Element in Windows Security dialog box disappeared before this program could interact with it, skipping this dialog box instance"); |
116 | 115 | } catch (COMException e) { |
117 | 116 | LOGGER.Error(e, "UI Automation error while selecting security key, skipping this dialog box instance"); |
118 | 117 | } catch (Exception e) when (e is not OutOfMemoryException) { |
119 | 118 | LOGGER.Error(e, "Uncaught exception while handling Windows Security dialog box, skipping it"); |
120 | 119 | } |
121 | 120 | } |
122 | 121 |
|
123 | | - // #5, #11: power series backoff, max=500 ms per attempt, ~1 minute total |
124 | | - private static T retry<T>(Func<T> attempt, int maxAttempts) => |
125 | | - Retrier.Attempt(_ => attempt(), maxAttempts, Retrier.Delays.Power(TimeSpan.FromMilliseconds(1), max: TimeSpan.FromMilliseconds(500))); |
126 | | - |
127 | 122 | // Window name and title are localized, so don't match against those |
128 | 123 | public override bool isFidoPromptWindow(SystemWindow window) => window.ClassName == WINDOW_CLASS_NAME; |
129 | 124 |
|
|
0 commit comments