-
Notifications
You must be signed in to change notification settings - Fork 67
Fix #592: allow multiple readers but no multiple scans on any reader #611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused with the algorithm.
See my notes.
Signed-off-by: Zoltan Kis <[email protected]>
Signed-off-by: Zoltan Kis <[email protected]>
index.html
Outdated
</li> | ||
<li> | ||
Clear the <a>pending read tuple</a>. | ||
Attempt to <a>abort a pending scan operation</a>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should also abort steps below when promise gets rejected right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand.
index.html
Outdated
@@ -3419,6 +3440,9 @@ <h3><dfn>Writing content</dfn></h3> | |||
browsing context</a>, then reject |p| with and | |||
{{"InvalidStateError"}} {{DOMException}} and return |p|. | |||
</li> | |||
<li> | |||
Attempt to <a>abort a pending scan operation</a>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, we should return |p| with rejected promise if failing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, we just reject the pending scan, not this one. It's a cleanup operation.
Or do you mean if the abort fails, we should reject the scan?
i.e. should we handle the "ongoing operation" step as a fail to abort and propagate it up?
To be clear, here are web platform tests for this issue: nfc_test(async (t, mockNFC) => {
const ndef = new NDEFReader();
const promise1 = ndef.scan();
const promise2 = promise_rejects_dom(t, 'InvalidStateError', ndef.scan());
await promise1;
await promise2;
}, "Test that NDEFReader.scan rejects if there is already an ongoing scan.");
nfc_test(async (t, mockNFC) => {
const ndef = new NDEFReader();
const controller = new AbortController();
await ndef.scan({signal : controller.signal});
controller.abort();
await ndef.scan();
}, "Test that NDEFReader.scan can be started after the previous scan is aborted."); |
@zolkis rightly pointed out that write behaviour is different: nfc_test(async (t, mockNFC) => {
const ndef1 = new NDEFReader();
const ndef2 = new NDEFReader();
const p1 = ndef1.write(test_text_data, {overwrite: false});
const p2 = ndef2.write(test_url_data, {overwrite: true});
await new Promise((resolve, reject) => {
// Make first push pending
mockNFC.setPendingPushCompleted(false);
let err = "";
p1.then(() => {
reject("pending push should not be fulfilled");
}).catch(e => {
err = e.name;
});
p2.then(() => {
assertNDEFMessagesEqual(test_url_data, mockNFC.pushedMessage());
assertNDEFWriteOptionsEqual( {overwrite: true}, mockNFC.writeOptions());
assert_equals(err, "AbortError", "the pending push should be aborted");
resolve();
});
});
}, "NDEFReader.write should replace all previously configured write operations."); |
I think it makes more sense to replace the previous ops with the current ones, rather than failing the current ones if old ones are ongoing. Saves an explicit abort from the app script. But I am open to be convinced. |
What's next? Merge this and update tests? |
+1 consistency |
Consistency which way? If we align to write behavior, this PR is correct. As the title also tells, scan rejects prior scans, i.e. replaces them. |
In example below, how does user know that const ndef1 = new NDEFReader();
await ndef1.scan(); // Promise resolves and reading occurs...
ndef1.onreading => ({message}) => { /* Handle NDEF message */}
// few seconds later
const ndef2 = new NDEFReader();
await ndef2.scan(); // Promise resolves and reading occurs
ndef2.onreading => ({message}) => { /* Handle NDEF message */}
// ndef2 will get reading events.
// How about ndef1? |
Valid observations. In this version of the API, and in this PR, if ndef1.scan() has already resolved and ndef2.scan() is called, the events are NOT coming through ndef1's event since the "abort a pending scan" algorithm steps. But we don't have a way to notify ndef1 about it having been stopped. Using the onreadingerror event would be a misuse of it. Another pain point in this design. Could be avoided if we had a direct API like resolving the scan() promise with the data read. However, that would not support reading multiple tags (like a design with events does), unless that promise resolved with an array of NDEFMessage's. (Sorry, this was edited twice). |
On a second thought, we could use an |
According to the discussion in #592, at this point of time people prefer to fail scans if there is an existing scan, but new writes will continue replace pending writes. The reason is that scans report data through an event, which is different from writes. |
Signed-off-by: Zoltan Kis <[email protected]>
I believe the See JS code below for my expectations. const ndef1 = new NDEFReader();
await ndef1.scan(); // Promise resolves and reading occurs...
ndef1.onreading => ({message}) => { /* Handle NDEF message */}
// few seconds later
const ndef2 = new NDEFReader();
await ndef2.scan(); // Promise resolves and reading occurs
ndef2.onreading => ({message}) => { /* Handle NDEF message */}
// ndef2 will get reading events.
// ndef1 will also continue to get reading events. const ndef = new NDEFReader();
const p1 = ndef.scan(); // Promise resolves.
const p2 = ndef.scan(); // Promise rejects right away as there's already a pending scan request |
@riju can you check my sayings? |
No, the spec has stated for ages it was on the settings object, i.e. tied to NfcProxy / NfcImpl, not the readers. The scan sequence diagram also reinforces that. |
There cannot be a pending read tuple per reader, since then we would store the promise + reader in the reader, which makes no sense. What we could do in the spec is return to the "activated reader list" internal slot, since the implementation still has that one (meaning the "pending read tuple" internal slot was never implemented). In the Chromium implementation, For now, I will try to align the spec to the existing implementation, but this will need to be cleaned up in the future. |
…iple readers Signed-off-by: Zoltan Kis <[email protected]>
Yet another change in this PR scope (and name). Now we allow multiple readers, but at each reader, successive scan() invocations will fail. |
Signed-off-by: Zoltan Kis <[email protected]>
65b8e90
to
ea613a0
Compare
@zolkis Please have a look at https://chromium-review.googlesource.com/c/chromium/src/+/2297559/5/third_party/blink/renderer/modules/nfc/ndef_reader.cc and ask help from @riju, the |
Do you refer to this? https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/modules/nfc/ndef_reader.cc;drc=aca54053714d442febdd5f87a26c4055e20bd632;l=187 But then see that the condition is bound to the existence of the reader after it checked was there a pending scan request. Also the design doc states there is only one scan in a reader active at any time. Could you point out which algorithmic steps are in conflict with the implementation? |
Signed-off-by: Zoltan Kis <[email protected]>
We had a call with Arno and Riju, and that clarified things. I discovered a non-related bug in the scan algo, fixed in the commit above. The In the scan algorithm in the spec is IMHO correct: the condition on The existing WPT should be working as defined with the current specification of the scan algorithm. |
Crashing will only happen on Debug builds (hence DCHECK) and is only for chromium developers to debug non expected cases.
I believe we should update one web platform test to make sure it's fully working: nfc_test(async (t, mockNFC) => {
const ndef = new NDEFReader();
const controller = new AbortController();
await ndef.scan({signal : controller.signal});
+ await promise_rejects_dom(t, 'InvalidStateError', ndef.scan());
controller.abort();
await ndef.scan();
}, "Test that NDEFReader.scan can be started after the previous scan is aborted."); |
Co-authored-by: François Beaufort <[email protected]>
Thanks for the updates.
Description needs update as well? Isn't that testing 3 scans? Tests if the second scan() fails since the first is started, and the third can be started once the first is aborted? Maybe split in 2 test cases? |
I've ended up updating another test. See web-platform-tests/wpt#27014 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @zolkis!
Signed-off-by: Zoltan Kis [email protected]
Preview | Diff