-
-
Notifications
You must be signed in to change notification settings - Fork 112
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
fix(web): gracefully disable predictive-text when WebWorkers are unavailable #13270
base: beta
Are you sure you want to change the base?
Conversation
User Test ResultsTest specification and instructions Test Artifacts
|
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 am a little uncomfortable with how lngProcessor
has now become optional without any clear maintenance documentation about this -- it feels a bit fragile.
And a question about the worker.
@@ -48,7 +48,11 @@ export class InputProcessor { | |||
|
|||
this.contextDevice = device; | |||
this.kbdProcessor = new KeyboardProcessor(device, options); | |||
this.lngProcessor = new LanguageProcessor(predictiveTextWorker, this.contextCache); | |||
try { | |||
this.lngProcessor = new LanguageProcessor(predictiveTextWorker, this.contextCache); |
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.
This puzzles me, isn't the issue with Yeti that we cannot construct predictiveTextWorker
, so wouldn't this go wrong (much) earlier?
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.
keyman/web/src/engine/predictive-text/worker-main/src/web/sourcemappedWorker.ts
Lines 4 to 10 in 263f918
export default class SourcemappedWorker { | |
// the only difference to DefaultWorker is that this class uses | |
// the unminified LM* blobs | |
static constructInstance(): Worker { | |
return new Worker(this.asBlobURI(LMLayerWorkerCode)); | |
} |
The issue is that we cannot allow line 9 above to run - we can't instantiate the actual Worker
. predictiveTextWorker
above corresponds to an instance of the class SourcemappedWorker
here. I suppose we could add Factory
to the variable and parameter names to make the design pattern clearer.
There's also a Node-friendly variant for use in headless testing, which is why the abstraction exists:
keyman/web/src/engine/predictive-text/worker-main/src/node/sourcemappedWorker.ts
Lines 6 to 15 in 263f918
export default class SourcemappedWorker { | |
static constructInstance(): Worker { | |
let scriptStr = unwrap(LMLayerWorkerCode); | |
// If this is definitively set to either true or false, tree-shaking can take effect. | |
// An imported const variable doesn't seem to do it, though. | |
// if(false) { | |
scriptStr += '\n' + LMLayerWorkerSourcemapComment; | |
// } | |
let worker = new MappedWorker(scriptStr); |
MappedWorker
there uses Node workers internally, but their spec differs enough from the Web worker type that we needed special handling for it.
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.
Looking at where constructInstance()
is called, it seems to be called at the very top-level, e.g.
keyman/web/src/app/browser/src/release-main.ts
Lines 11 to 16 in 6591c61
var scripts = document.getElementsByTagName('script'); | |
var ss = scripts[scripts.length-1].src; | |
var sPath = ss.substr(0,ss.lastIndexOf('/')+1); | |
// @ts-ignore | |
window['keyman'] = new KeymanEngine(Worker.constructInstance(), sPath); |
So I am not sure how this fix handles that?
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.
Huh. Makes me wonder why it seemed to work with the artificial repro attempt I tried. 🤷 Good point.
Test ResultsI tested this issue with the attached Keyman"18.0.195-beta-test-13270" build(18/02/2025) on Android 14. Here I am sharing my observation.
|
I've clarified the test results with @dinakaranr - it sounds like our |
Also wondering if the lngProcessor should always exist and just have a 'disabled' state (unless 'inactive' is sufficient)? Feels less fragile than lots of optional tests and encapsulates its behavior better. And it would be cleaner to keep the try/catch as close as possible around the instance construction. It also looks like LanguageProcessor already has an inactive state where |
I opted for the strategy I did because the approach you're suggesting then motivates a question: what should the class's active methods do if called when the engine is disabled? My intuition says "throw errors", and that is prone to its own form of fragility and instability - we'd want to either try-catch them elsewhere or add a lot of I personally felt that relying on the behavior of optionals encapsulated the same logic more cleanly and succinctly while allowing us to get away with a single try-catch. That said, how would you answer my first paragraph's question? What would be the design for that? |
Given that the way the class is being used is to ignore it if it isn't available, I would design the internals of the class that way -- if it is disabled, active methods become a no-op. Allow the class to be interrogated as to its state (in case we need that somewhere; for completeness); document that when the class is in disabled state, actions are a no-op, not an error. That makes the design explicit, separates the concerns of availability of the worker, and avoids the fragility of having to nullish-check the class every usage. |
I've made a replacement PR as #13290. |
A bit more on this - it's Yeti 1.1 that shows up in the error reports. Meanwhile, Google's Play Store mentions Yeti being at version 1.5. I'm guessing they added WebWorker support after a while. |
Fixes: #13262
User Testing
TEST_ANDROID_YETI_WITH_WEB: Using an Android device, verify that our test pages operate normally when using the Yeti browser.