@@ -5905,7 +5905,9 @@ export function preloadResource(resource: Resource): boolean {
59055905
59065906type SuspendedState = {
59075907 stylesheets : null | Map < StylesheetResource , HoistableRoot > ,
5908- count : number ,
5908+ count : number , // suspensey css and active view transitions
5909+ imgCount : number , // suspensey images
5910+ waitingForImages : boolean , // false when we're no longer blocking on images
59095911 unsuspend : null | ( ( ) => void ) ,
59105912} ;
59115913let suspendedState : null | SuspendedState = null ;
@@ -5914,6 +5916,8 @@ export function startSuspendingCommit(): void {
59145916 suspendedState = {
59155917 stylesheets : null ,
59165918 count : 0 ,
5919+ imgCount : 0 ,
5920+ waitingForImages : true ,
59175921 // We use a noop function when we begin suspending because if possible we want the
59185922 // waitfor step to finish synchronously. If it doesn't we'll return a function to
59195923 // provide the actual unsuspend function and that will get completed when the count
@@ -5922,6 +5926,8 @@ export function startSuspendingCommit(): void {
59225926 } ;
59235927}
59245928
5929+ const SUSPENSEY_STYLESHEET_TIMEOUT = 60000 ;
5930+
59255931const SUSPENSEY_IMAGE_TIMEOUT = 500 ;
59265932
59275933export function suspendInstance (
@@ -5946,13 +5952,10 @@ export function suspendInstance(
59465952 // If this browser supports decode() API, we use it to suspend waiting on the image.
59475953 // The loading should have already started at this point, so it should be enough to
59485954 // just call decode() which should also wait for the data to finish loading.
5949- state . count ++ ;
5950- const ping = onUnsuspend . bind ( state ) ;
5951- Promise . race ( [
5952- // $FlowFixMe[prop-missing]
5953- instance . decode ( ) ,
5954- new Promise ( resolve => setTimeout ( resolve , SUSPENSEY_IMAGE_TIMEOUT ) ) ,
5955- ] ) . then ( ping , ping ) ;
5955+ state . imgCount ++ ;
5956+ const ping = onUnsuspendImg . bind ( state ) ;
5957+ // $FlowFixMe[prop-missing]
5958+ instance . decode ( ) . then ( ping , ping ) ;
59565959 }
59575960}
59585961
@@ -6087,7 +6090,7 @@ export function waitForCommitToBeReady(
60876090
60886091 // We need to check the count again because the inserted stylesheets may have led to new
60896092 // tasks to wait on.
6090- if ( state . count > 0 ) {
6093+ if ( state . count > 0 || state . imgCount > 0 ) {
60916094 return commit => {
60926095 // We almost never want to show content before its styles have loaded. But
60936096 // eventually we will give up and allow unstyled content. So this number is
@@ -6104,37 +6107,62 @@ export function waitForCommitToBeReady(
61046107 state . unsuspend = null ;
61056108 unsuspend ( ) ;
61066109 }
6107- } , 60000 + timeoutOffset ) ; // one minute
6110+ } , SUSPENSEY_STYLESHEET_TIMEOUT + timeoutOffset ) ;
6111+
6112+ const imgTimer = setTimeout ( ( ) => {
6113+ // We're no longer blocked on images. If CSS resolves after this we can commit.
6114+ state . waitingForImages = false ;
6115+ if ( state . count === 0 ) {
6116+ if ( state . stylesheets ) {
6117+ insertSuspendedStylesheets ( state , state . stylesheets ) ;
6118+ }
6119+ if ( state . unsuspend ) {
6120+ const unsuspend = state . unsuspend ;
6121+ state . unsuspend = null ;
6122+ unsuspend ( ) ;
6123+ }
6124+ }
6125+ } , SUSPENSEY_IMAGE_TIMEOUT + timeoutOffset ) ;
61086126
61096127 state . unsuspend = commit ;
61106128
61116129 return ( ) => {
61126130 state . unsuspend = null ;
61136131 clearTimeout ( stylesheetTimer ) ;
6132+ clearTimeout ( imgTimer ) ;
61146133 } ;
61156134 } ;
61166135 }
61176136 return null ;
61186137}
61196138
6120- function onUnsuspend ( this : SuspendedState ) {
6121- this . count -- ;
6122- if ( this . count === 0 ) {
6123- if ( this . stylesheets ) {
6139+ function checkIfFullyUnsuspended ( state : SuspendedState ) {
6140+ if ( state . count === 0 && ( state . imgCount === 0 || ! state . waitingForImages ) ) {
6141+ if ( state . stylesheets ) {
61246142 // If we haven't actually inserted the stylesheets yet we need to do so now before starting the commit.
61256143 // The reason we do this after everything else has finished is because we want to have all the stylesheets
61266144 // load synchronously right before mutating. Ideally the new styles will cause a single recalc only on the
61276145 // new tree. When we filled up stylesheets we only inlcuded stylesheets with matching media attributes so we
61286146 // wait for them to load before actually continuing. We expect this to increase the count above zero
6129- insertSuspendedStylesheets ( this , this . stylesheets ) ;
6130- } else if ( this . unsuspend ) {
6131- const unsuspend = this . unsuspend ;
6132- this . unsuspend = null ;
6147+ insertSuspendedStylesheets ( state , state . stylesheets ) ;
6148+ } else if ( state . unsuspend ) {
6149+ const unsuspend = state . unsuspend ;
6150+ state . unsuspend = null ;
61336151 unsuspend ( ) ;
61346152 }
61356153 }
61366154}
61376155
6156+ function onUnsuspend ( this : SuspendedState ) {
6157+ this . count -- ;
6158+ checkIfFullyUnsuspended ( this ) ;
6159+ }
6160+
6161+ function onUnsuspendImg ( this : SuspendedState ) {
6162+ this . imgCount -- ;
6163+ checkIfFullyUnsuspended ( this ) ;
6164+ }
6165+
61386166// We use a value that is type distinct from precedence to track which one is last.
61396167// This ensures there is no collision with user defined precedences. Normally we would
61406168// just track this in module scope but since the precedences are tracked per HoistableRoot
0 commit comments