also looking at this
fix(android): tie a ready mark to its own navigation, and inject a document once
#239
5 files
+159
-3
| @@ -381,6 +381,47 @@ | ||
| 381 | 381 | }) |
| 382 | 382 | } |
| 383 | 383 | }, |
| 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 | }, | |
| 384 | 425 | { |
| 385 | 426 | // #209: a capability the app was built with is served, and the page |
| 386 | 427 | // can tell which is which from capabilities alone. Sharing is on in |