Bug 265386

Summary: Streaming HTML only works in iframe elements. Safari waits to buffer all HTML before rendering.
Product: WebKit Reporter: Nathan Knowler <nathan>
Component: Layout and RenderingAssignee: Nobody <webkit-unassigned>
Status: NEW ---    
Severity: Normal CC: bfulgham, konnor5456, mike, rik, simon.fraser, webkit-bug-importer, zalan
Priority: P2 Keywords: InRadar
Version: Safari 17   
Hardware: Mac (Apple Silicon)   
OS: macOS 13   
Attachments:
Description Flags
Deno Server which streams HTML. Run with `deno run --allow-net deno-server-streaming-html.js`.
none
Source code of the hosted Deno server which streaming to the Declarative Shadow DOM. none

Description Nathan Knowler 2023-11-27 09:03:32 PST
Created attachment 468769 [details]
Deno Server which streams HTML. Run with `deno run --allow-net deno-server-streaming-html.js`.

Safari doesn’t seem to be streaming HTML at all, except for `<iframe>` elements. Right now, it appears to buffer all the HTML before rendering anything.

To reproduce you will need a web server that supports streaming. The attached example uses Deno (1.38.2). When run locally, it’ll serve HTTP/1.1 and adds the correct `Transport-Encoding: chunked` header. I have a hosted variation of the example which is being served with Deno Deploy: https://shadowroot-streams.deno.dev. It also uses the Declarative Shadow DOM. That example uses HTTP/2 and correctly doesn’t use the `Transport-Encoding` header since it’s incompatible with HTTP/2. In short, Deno’s streaming the content correctly and Deno Deploy supports HTML streaming.

Note: the attached example admittedly doesn’t have a tonne of HTML in the first chunk, but that doesn’t seem to matter at all for streaming into an `<iframe>` element. Other browsers are also able to stream this fine.
Comment 1 Nathan Knowler 2023-11-27 09:05:10 PST
Created attachment 468770 [details]
Source code of the hosted Deno server which streaming to the Declarative Shadow DOM.
Comment 2 Radar WebKit Bug Importer 2023-11-27 09:39:50 PST
<rdar://problem/118837578>
Comment 3 Konnor Rogers 2024-09-25 14:05:37 PDT
To add to this, the problem is very specific.

Safari seems to need about 512bytes of HTML to start streaming, otherwise it will remain buffered as noted here:

https://lamplightdev.com/blog/2024/01/10/streaming-html-out-of-order-without-javascript/


*BUT* the problem is even more speific than "512 bytes", Safari seems to want 512 bytes of displayable HTML.

That is, any element in the `<head>` is ignored, any element that is default hidden IE: `<script>`, `<template>`, HTML comments, etc are all ignored. Even `<canvas>` with text in it is ignored.

The "easiest" way of triggering HTML streaming seems to be with a height / width of 0 div, with no overflow. Like this:

```html
<body>
<div style="height: 0; width: 0; overflow: hidden;" hidden>
 {{ 512 bytes of data }}
</div>
<!-- stream the rest -->
</body>
```

This seems to be Safari / WebKit specific and is a pretty big gotcha.
Comment 4 Konnor Rogers 2024-09-25 14:11:44 PDT
Sorry, `<div hidden>` actually causes it to still buffer. So you need to use aria-hidden instead.

```html
<body>
<div style="height: 0; width: 0; overflow: hidden;" aria-hidden="true">
 {{ 512 bytes of data }}
</div>
<!-- stream the rest -->
</body>
```
Comment 5 Konnor Rogers 2024-09-25 14:57:01 PDT
As it turns out, the first element in the body doesn't seem to count. So its even worse than that. The above code will not stop buffering. But it will work if you inject it into the shadowroot of the body.

Here's the secret incantation Joel Drapper came up with to skirt Safari's HTML streaming. To note, Safari expects 512 characters, and not 512 bytes, so there's 512 escape sequences for zero width white space.

```html
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <template shadowrootmode="open">
      <span aria-hidden="true" style="-webkit-user-select: none; user-select: none;">
&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203&#8203
</span>
    </template>
  </body>
</html>
```