ReviewOS

also looking at this

craft-native/craft

fix(android): tie a ready mark to its own navigation, and inject a document once

#239
Merged glennmichael123 wants to merge fix/android-injection-generation into main
5 files +159 -3
scripts/mobile-e2e/driver.htmlmodified+41-0
Changes to scripts/mobile-e2e/driver.html
@@ -381,6 +381,47 @@
381381 })
382382 }
383383 },
384 {
385 // #228: Chromium calls onPageFinished without a matching
386 // onPageStarted for a same-document navigation, and MainActivity
387 // injects the bridge on every one of them. Whether this emulator
388 // does it is what the issue asked to find out; either way the page
389 // must come out of it with the bridge it started with, because a
390 // second run of the script replaces window.craft, fires craftReady
391 // again, and rejects everything in flight.
392 //
393 // The wait is what makes this a real check: onPageFinished arrives
394 // asynchronously, so a case that asserted immediately would pass
395 // without having given the WebView a chance to re-inject.
396 name: 'bridge.survivesSameDocumentNavigation',
397 platforms: ['android'],
398 run: function () {
399 var installed = window.craft
400 // The runtime that holds every pending promise. A second injection
401 // replaces it, and on the way out rejects them all with "Android
402 // bridge reinitialized" — which is the damage this case is about,
403 // and is visible here without needing a call that stays pending
404 // for the whole wait.
405 var runtime = window.__craftPromise
406 var readyAgain = 0
407 var onReady = function () { readyAgain += 1 }
408 window.addEventListener('craftReady', onReady)
409
410 history.pushState({}, '', location.pathname + '?craft-e2e-pushstate=1')
411
412 return new Promise(function (resolve) { setTimeout(resolve, 2000) }).then(function () {
413 window.removeEventListener('craftReady', onReady)
414 if (window.craft !== installed) return 'window.craft was replaced by a second injection'
415 if (window.__craftPromise !== runtime) return 'the promise runtime was replaced, rejecting everything in flight'
416 if (readyAgain !== 0) return 'craftReady fired ' + readyAgain + ' more time(s) in the same document'
417 // And the bridge still works afterwards, so the guard cannot pass
418 // by having stopped the bridge from existing at all.
419 return withTimeout(function () { return window.craft.clipboard.read() }).then(function (outcome) {
420 if (outcome.state !== 'resolved') return 'the bridge stopped answering after the navigation'
421 })
422 })
423 }
424 },
384425 {
385426 // #209: a capability the app was built with is served, and the page
386427 // can tell which is which from capabilities alone. Sharing is on in