- WebKit handles streaming HTTP responses very differently from other browsers. - First we thought that it has a bigger minimum byte count to accept the initially flushed response but we have disproven that. - - You can add arbitrary bytes to the head, Webkit will still not paint early. - Instead there seems to be a heuristic in place that requires a certain number of "contentful pixels" to be present to perform an initial paint. - The way I established that is by making a demo which allows controlling the text amount in the initial streamed chunk via a query string. Compare these two URLs and how they behave on Safari vs. Chrome (I tested on Desktop): - https://streaming-threshold.vercel.app/api/stream?count=2000 - https://streaming-threshold.vercel.app/api/stream?count=1000 Note how they differ in the count of the "a" character. If you increase the font size of the "a" you need fewer to trigger the heuristic 🤯 I assume that the heuristic is in place to avoid certain "flashes of incomplete content". However, given that other browsers don't have this issue, it might make sense to align behavior with them. The primary consequence of the heuristic is that naive streaming demos don't work in Safari. This is likely not a problem for most real web pages that have more content, but we originally found this with a graphically sophisticated page that had little "content".
They way the trade-off is described in the report makes me think that what we do now is the right choice?
This is about our first paint heuristic (we don't generally refer to this being about "streaming HTML").
It would be better for someone on the rendering team to chime in on this. But the visually non empty heuristic (FrameView::checkAndDispatchDidReachVisuallyNonEmptyState) is indeed there to try and prevent flashes of white when navigating between pages, and also to save resources on paints that will be quickly replaced by other paints. The thresholds are already pretty low so I'm not sure that we should be decreasing them even more for real webpages.
This is likely a different threshold than the one to prevent white-paints between navigations (I'm a big fan of that one!) Here we are talking about Safari not painting for 5 seconds+ while there is stuff to paint and other browsers paint. Are you aware of a URL that behaves better with the heuristic? Chrome's heuristic is: - paint after 65KB - paint before encountering a sync script tag in HTML body The latter triggers paints at the right time for React's and Svelte's streaming implementations, because they always emit a script tag when they flush the buffer.
<rdar://problem/105845237>
See https://github.com/vercel/next.js/issues/52444 for a reproducer