refactor(encoding): rename shared document streaming decoder

This commit is contained in:
ldm0
2026-10-09 01:59:04 +08:00
committed by Donough Liu
parent c884fe059b
commit 5c44cd6eff
6 changed files with 63 additions and 60 deletions
+7 -3
View File
@@ -22,7 +22,7 @@ pub fn decode_html_document_with_fallback(
headers: &[(String, Vec<u8>)],
fallback_encoding: Option<&str>,
) -> (String, &'static str) {
let mut decoder = HtmlDocumentStreamingDecoder::new_with_fallback(headers, fallback_encoding);
let mut decoder = DocumentStreamingDecoder::new_with_fallback(headers, fallback_encoding);
let mut output = String::new();
for chunk in decoder.push(bytes) {
output.push_str(&chunk);
@@ -43,7 +43,11 @@ enum DocumentDeclarationPolicy {
Text,
}
pub struct HtmlDocumentStreamingDecoder {
/// Streaming decoder for HTML, XML, and text document responses.
///
/// [`Self::new`] uses HTML declaration rules. XML and text responses use
/// [`Self::new_xml_document`] and [`Self::new_text_document`] respectively.
pub struct DocumentStreamingDecoder {
transport_encoding: Option<&'static Encoding>,
fallback_encoding: &'static Encoding,
sniff_buffer: Vec<u8>,
@@ -57,7 +61,7 @@ pub struct HtmlDocumentStreamingDecoder {
declaration_policy: DocumentDeclarationPolicy,
}
impl HtmlDocumentStreamingDecoder {
impl DocumentStreamingDecoder {
/// Decode a text document without interpreting literal HTML/XML declarations.
/// BOMs and transport charsets take precedence. JSON then defaults to UTF-8;
/// other text uses a valid inherited encoding before heuristic detection.
+1 -1
View File
@@ -9,7 +9,7 @@ mod query;
mod script;
pub use document::{
HtmlDocumentStreamingDecoder, LegacyEncodingDetector, decode_html_document,
DocumentStreamingDecoder, LegacyEncodingDetector, decode_html_document,
decode_html_document_with_fallback,
};
pub use form::{
+32 -33
View File
@@ -49,14 +49,14 @@ fn assert_xml_document_decodes_across_chunk_splits(
encoding: &str,
) {
for split in 0..=bytes.len() {
let mut decoder = HtmlDocumentStreamingDecoder::new_xml_document(headers);
let mut decoder = DocumentStreamingDecoder::new_xml_document(headers);
let mut decoded = decoder.push(&bytes[..split]).concat();
decoded.push_str(&decoder.push(&bytes[split..]).concat());
decoded.push_str(&decoder.finish().unwrap_or_default());
assert_eq!(decoded, source, "{encoding}: split {split}");
assert_eq!(decoder.document_encoding_name(), encoding, "split {split}");
}
let mut decoder = HtmlDocumentStreamingDecoder::new_xml_document(headers);
let mut decoder = DocumentStreamingDecoder::new_xml_document(headers);
let mut decoded = String::new();
for byte in bytes {
decoded.push_str(&decoder.push(std::slice::from_ref(byte)).concat());
@@ -107,7 +107,7 @@ fn xml_document_ignores_encoding_attributes_in_processing_instructions() {
#[test]
fn xml_document_selects_encoding_at_the_end_of_its_declaration() {
let mut decoder = HtmlDocumentStreamingDecoder::new_xml_document(&[]);
let mut decoder = DocumentStreamingDecoder::new_xml_document(&[]);
let mut decoded = decoder
.push(b"<?xml version='1.0' encoding='windows-")
.concat();
@@ -141,10 +141,10 @@ fn xml_document_finishes_empty_or_incomplete_declarations_with_utf8() {
fn chromium_xml_declaration_pieces_follow_the_document_policy() {
let source = b"<?xml encoding='utf-8'?>foo";
for (mut decoder, expected_encoding) in [
(HtmlDocumentStreamingDecoder::new(&[]), "UTF-8"),
(HtmlDocumentStreamingDecoder::new_xml_document(&[]), "UTF-8"),
(DocumentStreamingDecoder::new(&[]), "UTF-8"),
(DocumentStreamingDecoder::new_xml_document(&[]), "UTF-8"),
(
HtmlDocumentStreamingDecoder::new_text_document(
DocumentStreamingDecoder::new_text_document(
&[],
"https://example.test/",
test_legacy_encoding_detector,
@@ -172,7 +172,7 @@ fn chromium_plain_text_broken_boms_are_flushed_with_the_default_encoding() {
(b"\xff", "ÿ"),
(b"\xfe", "þ"),
] {
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&[],
"https://example.test/",
test_legacy_encoding_detector,
@@ -209,7 +209,7 @@ fn chromium_bom_in_content_is_preserved_by_xml_decoding() {
fn json_text_document_defaults_to_utf8_across_every_chunk_split() {
let input = "{\"name\":\"Gülçek\"}";
for split in 0..=input.len() {
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&[],
"https://example.test/",
unexpected_legacy_encoding_detector,
@@ -230,7 +230,7 @@ fn plain_text_document_honors_transport_charset() {
"Content-Type".to_owned(),
b"text/plain; charset=gbk".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&headers,
"https://example.test/",
unexpected_legacy_encoding_detector,
@@ -249,7 +249,7 @@ fn text_document_bom_precedes_transport_charset() {
"Content-Type".to_owned(),
b"text/plain; charset=gbk".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&headers,
"https://example.test/",
unexpected_legacy_encoding_detector,
@@ -271,7 +271,7 @@ fn text_document_bom_precedes_transport_charset() {
#[test]
fn plain_text_document_retains_legacy_detection_without_a_charset() {
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&[],
"https://legacy.example/",
test_legacy_encoding_detector,
@@ -293,7 +293,7 @@ fn plain_text_document_inherits_encoding_before_detection_across_chunk_splits()
] {
let bytes = encoding.encode(input).0;
for split in 0..=bytes.len() {
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&[],
"https://example.test/",
unexpected_legacy_encoding_detector,
@@ -312,7 +312,7 @@ fn plain_text_document_inherits_encoding_before_detection_across_chunk_splits()
#[test]
fn invalid_inherited_text_encoding_does_not_disable_detection() {
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&[],
"https://legacy.example/",
test_legacy_encoding_detector,
@@ -333,7 +333,7 @@ fn plain_text_document_ignores_literal_encoding_declarations() {
] {
let mut input = prefix.as_bytes().to_vec();
input.push(0xe9);
let mut decoder = HtmlDocumentStreamingDecoder::new_text_document(
let mut decoder = DocumentStreamingDecoder::new_text_document(
&[],
"https://example.test/",
test_legacy_encoding_detector,
@@ -353,7 +353,7 @@ fn content_type_charset_is_selected() {
"Content-Type".to_owned(),
b"text/html; charset=gbk".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert_eq!(decoder.push(&gbk_bytes("太平洋")), vec!["太平洋"]);
assert_eq!(decoder.selected_encoding_name(), Some("GBK"));
@@ -364,7 +364,7 @@ fn meta_charset_is_selected_without_header_charset() {
let headers: Vec<(String, Vec<u8>)> = vec![("Content-Type".to_owned(), b"text/html".to_vec())];
let mut input = b"<!doctype html><meta charset=\"gbk\"><p>".to_vec();
input.extend_from_slice(&gbk_bytes("家居"));
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert_eq!(
decoder.push(&input),
@@ -376,7 +376,7 @@ fn meta_charset_is_selected_without_header_charset() {
#[test]
fn meta_charset_can_be_split_across_chunks() {
let headers: Vec<(String, Vec<u8>)> = vec![("Content-Type".to_owned(), b"text/html".to_vec())];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert_eq!(
decoder.push(b"<!doctype html><meta char"),
@@ -392,7 +392,7 @@ fn meta_charset_can_be_split_across_chunks() {
#[test]
fn ascii_prefix_streams_while_charset_sniffing_continues() {
let headers: Vec<(String, Vec<u8>)> = vec![("Content-Type".to_owned(), b"text/html".to_vec())];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert_eq!(
decoder.push(b"<!doctype html><script src=\"/gate.js\"></script>"),
@@ -406,7 +406,7 @@ fn ascii_prefix_streams_while_charset_sniffing_continues() {
#[test]
fn later_meta_charset_decodes_unemitted_non_ascii_after_ascii_prefix() {
let headers: Vec<(String, Vec<u8>)> = vec![("Content-Type".to_owned(), b"text/html".to_vec())];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
let mut tail = b"<meta charset=\"gbk\"><p>".to_vec();
tail.extend_from_slice(&gbk_bytes("家居"));
@@ -421,7 +421,7 @@ fn meta_charset_after_1024_bytes_still_in_head_is_selected() {
let mut input = vec![b' '; HTML_META_CHARSET_PRESCAN_LIMIT];
input.extend_from_slice(b"<meta charset=\"gbk\"><p>");
input.extend_from_slice(&gbk_bytes("家居"));
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
let decoded = decoder.push(&input).join("");
@@ -437,7 +437,7 @@ fn meta_charset_crossing_1024_byte_boundary_is_selected_while_in_head() {
input.extend_from_slice(partial_meta);
input.extend_from_slice(b"set=\"gbk\"><p>");
input.extend_from_slice(&gbk_bytes("家居"));
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
let decoded = decoder.push(&input).join("");
@@ -452,7 +452,7 @@ fn meta_charset_after_1024_bytes_after_head_is_ignored() {
input.extend(vec![b' '; HTML_META_CHARSET_PRESCAN_LIMIT - input.len()]);
input.extend_from_slice(b"<meta charset=\"gbk\"><p>");
input.extend_from_slice(&gbk_bytes("家居"));
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
let decoded = decoder.push(&input).join("");
@@ -470,8 +470,7 @@ fn meta_charset_starting_before_1024_bytes_after_head_is_selected() {
]);
input.extend_from_slice(b"<meta charset=\"gbk\"><p>");
input.extend_from_slice(&gbk_bytes("家居"));
let mut decoder =
HtmlDocumentStreamingDecoder::new_with_fallback(&headers, Some("windows-1252"));
let mut decoder = DocumentStreamingDecoder::new_with_fallback(&headers, Some("windows-1252"));
let decoded = decoder.push(&input).join("");
@@ -523,7 +522,7 @@ fn gbk_multibyte_can_be_split_across_chunks() {
"Content-Type".to_owned(),
b"text/html; charset=gbk".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert!(decoder.push(&[0xCC]).is_empty());
assert_eq!(decoder.push(&[0xAB]), vec!["太"]);
@@ -535,7 +534,7 @@ fn bom_wins_over_header_charset() {
"Content-Type".to_owned(),
b"text/html; charset=gbk".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert_eq!(decoder.push(&[0xEF]), Vec::<String>::new());
assert_eq!(decoder.push(&[0xBB]), Vec::<String>::new());
@@ -549,7 +548,7 @@ fn unknown_charset_falls_back_to_html_default_on_finish() {
"Content-Type".to_owned(),
b"text/html; charset=x-unknown".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert_eq!(decoder.push(b"<p>ok"), vec!["<p>ok"]);
assert_eq!(decoder.finish(), None);
@@ -570,7 +569,7 @@ fn injected_legacy_content_detector_receives_url_hint() {
"Content-Type".to_owned(),
b"text/html; charset=x-unknown".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new_with_legacy_encoding_detector(
let mut decoder = DocumentStreamingDecoder::new_with_legacy_encoding_detector(
&headers,
"https://legacy.example/",
test_legacy_encoding_detector,
@@ -593,14 +592,14 @@ fn declared_encodings_precede_injected_legacy_detector() {
"Content-Type".to_owned(),
b"text/html; charset=gbk".to_vec(),
)];
let mut header_decoder = HtmlDocumentStreamingDecoder::new_with_legacy_encoding_detector(
let mut header_decoder = DocumentStreamingDecoder::new_with_legacy_encoding_detector(
&headers,
"https://legacy.example/",
unexpected_legacy_encoding_detector,
);
assert_eq!(header_decoder.push(&gbk_bytes("太平洋")), vec!["太平洋"]);
let mut meta_decoder = HtmlDocumentStreamingDecoder::new_with_legacy_encoding_detector(
let mut meta_decoder = DocumentStreamingDecoder::new_with_legacy_encoding_detector(
&[],
"https://legacy.example/",
unexpected_legacy_encoding_detector,
@@ -1023,7 +1022,7 @@ fn assert_bom_less_utf16_xml_decodes_across_signature_splits(
expected_encoding: &str,
) {
for split in 1..6 {
let mut decoder = HtmlDocumentStreamingDecoder::new(&[]);
let mut decoder = DocumentStreamingDecoder::new(&[]);
assert_eq!(
decoder.push(&input[..split]),
Vec::<String>::new(),
@@ -1065,7 +1064,7 @@ fn bom_less_utf16be_xml_signature_survives_every_streaming_split() {
#[test]
fn diverged_utf16_xml_signature_prefix_resumes_ascii_streaming() {
let mut decoder = HtmlDocumentStreamingDecoder::new(&[]);
let mut decoder = DocumentStreamingDecoder::new(&[]);
assert!(decoder.push(b"<").is_empty());
assert_eq!(
@@ -1082,7 +1081,7 @@ fn transport_charset_does_not_wait_for_a_utf16_xml_signature_prefix() {
"Content-Type".to_owned(),
b"text/html; charset=windows-1252".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
assert_eq!(decoder.push(b"<"), vec!["<"]);
assert_eq!(decoder.selected_encoding_name(), Some("windows-1252"));
@@ -1,4 +1,4 @@
use moli_encoding::HtmlDocumentStreamingDecoder;
use moli_encoding::DocumentStreamingDecoder;
use moli_encoding_detector::detect_legacy_html_encoding;
use moli_web_mime::{is_json_mime, is_text_document_mime, is_xml_document_mime};
use url::Url;
@@ -8,9 +8,9 @@ pub(crate) fn new_document_response_decoder(
content_type: Option<&str>,
final_url: &Url,
inherited_encoding: Option<&str>,
) -> HtmlDocumentStreamingDecoder {
) -> DocumentStreamingDecoder {
if let Some(mime) = content_type.filter(|mime| is_text_document_mime(mime)) {
HtmlDocumentStreamingDecoder::new_text_document(
DocumentStreamingDecoder::new_text_document(
headers,
final_url.as_str(),
detect_legacy_html_encoding,
@@ -18,11 +18,11 @@ pub(crate) fn new_document_response_decoder(
inherited_encoding,
)
} else if content_type.is_some_and(is_xml_document_mime) {
HtmlDocumentStreamingDecoder::new_xml_document(headers)
DocumentStreamingDecoder::new_xml_document(headers)
} else if inherited_encoding.is_some() {
HtmlDocumentStreamingDecoder::new_with_fallback(headers, inherited_encoding)
DocumentStreamingDecoder::new_with_fallback(headers, inherited_encoding)
} else {
HtmlDocumentStreamingDecoder::new_with_legacy_encoding_detector(
DocumentStreamingDecoder::new_with_legacy_encoding_detector(
headers,
final_url.as_str(),
detect_legacy_html_encoding,
@@ -4,7 +4,7 @@ use super::parser_blocking_pending::main_parser_blocking_classic_script_item;
use super::scaffold::continue_phase_one_until_streaming_boundary_on_execution_context;
use super::*;
use crate::document_response_decoder::{decode_document_response, new_document_response_decoder};
use moli_encoding::HtmlDocumentStreamingDecoder;
use moli_encoding::DocumentStreamingDecoder;
use moli_web_mime::response_headers_indicate_attachment_download;
use tokio::sync::{mpsc, oneshot};
@@ -620,7 +620,7 @@ fn response_document_parser(
final_url: Url,
headers: &[(String, Vec<u8>)],
env: &PageVmEnvConfig,
) -> (ParseTimeDriverState, HtmlDocumentStreamingDecoder) {
) -> (ParseTimeDriverState, DocumentStreamingDecoder) {
let content_type = moli_web_mime::response_document_content_type(headers);
let text_type = content_type
.as_deref()
@@ -643,7 +643,7 @@ fn response_document_parser(
pub(super) fn enqueue_streaming_raw_chunk(
runtime: &mut ConcurrentParseTimeRuntime,
decoder: &mut HtmlDocumentStreamingDecoder,
decoder: &mut DocumentStreamingDecoder,
chunk: Vec<u8>,
service_worker_context: Option<&ServiceWorkerScriptPreloadContext>,
) {
@@ -659,7 +659,7 @@ pub(super) fn enqueue_streaming_raw_chunk(
fn scan_prebootstrap_raw_chunk_into_state(
state: &mut ParseTimeDriverState,
decoder: &mut HtmlDocumentStreamingDecoder,
decoder: &mut DocumentStreamingDecoder,
loader: &ResourceRequestClient,
chunk: Vec<u8>,
service_worker_context: Option<&ServiceWorkerScriptPreloadContext>,
@@ -677,7 +677,7 @@ fn scan_prebootstrap_raw_chunk_into_state(
fn preload_scan_ready_streaming_raw_chunks(
runtime: &mut ConcurrentParseTimeRuntime,
response: &mut RawDocumentBodySource,
decoder: &mut HtmlDocumentStreamingDecoder,
decoder: &mut DocumentStreamingDecoder,
service_worker_context: Option<&ServiceWorkerScriptPreloadContext>,
) {
// Chromium keeps parser-visible tree construction paused while allowing the
@@ -692,7 +692,7 @@ fn preload_scan_ready_streaming_raw_chunks(
fn prebootstrap_scan_ready_streaming_raw_chunks(
state: &mut ParseTimeDriverState,
response: &mut RawDocumentBodySource,
decoder: &mut HtmlDocumentStreamingDecoder,
decoder: &mut DocumentStreamingDecoder,
loader: &ResourceRequestClient,
service_worker_context: Option<&ServiceWorkerScriptPreloadContext>,
) -> Vec<String> {
@@ -724,7 +724,7 @@ async fn settle_streaming_raw_response_at_boundary(
bootstrap_outcome: ParseTimePageVmStreamingBootstrapOutcome,
response: RawDocumentBodySource,
prebootstrap_chunks: Vec<String>,
decoder: HtmlDocumentStreamingDecoder,
decoder: DocumentStreamingDecoder,
service_worker_context: Option<ServiceWorkerScriptPreloadContext>,
started: Instant,
boundary: CommittedNavigationBootstrapBoundary,
@@ -758,7 +758,7 @@ fn park_streaming_raw_response_at_document_commit(
bootstrap_outcome: ParseTimePageVmStreamingBootstrapOutcome,
response: RawDocumentBodySource,
prebootstrap_chunks: Vec<String>,
decoder: HtmlDocumentStreamingDecoder,
decoder: DocumentStreamingDecoder,
service_worker_context: Option<ServiceWorkerScriptPreloadContext>,
started: Instant,
) -> Result<ParseTimePageVmCreationOutcome> {
@@ -803,7 +803,7 @@ async fn drive_streaming_raw_response_through_phase_one(
bootstrap_outcome: ParseTimePageVmStreamingBootstrapOutcome,
mut response: RawDocumentBodySource,
prebootstrap_chunks: Vec<String>,
mut decoder: HtmlDocumentStreamingDecoder,
mut decoder: DocumentStreamingDecoder,
service_worker_context: Option<ServiceWorkerScriptPreloadContext>,
started: Instant,
) -> Result<ParseTimePageVmCreationOutcome> {
@@ -900,7 +900,7 @@ async fn drive_streaming_raw_response_through_phase_one(
pub(super) fn sync_document_character_set_from_decoder(
runtime: &mut ConcurrentParseTimeRuntime,
decoder: &HtmlDocumentStreamingDecoder,
decoder: &DocumentStreamingDecoder,
) {
let encoding = decoder.document_encoding_name();
runtime.state.document_character_set = encoding.to_owned();
@@ -913,7 +913,7 @@ pub(super) fn sync_document_character_set_from_decoder(
fn sync_state_document_character_set_from_decoder(
state: &mut ParseTimeDriverState,
decoder: &HtmlDocumentStreamingDecoder,
decoder: &DocumentStreamingDecoder,
) {
let encoding = decoder.document_encoding_name();
state.document_character_set = encoding.to_owned();
@@ -1346,7 +1346,7 @@ mod tests {
"Content-Type".to_owned(),
b"text/html; charset=utf-8".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
preload_scan_ready_streaming_raw_chunks(&mut runtime, &mut source, &mut decoder, None);
@@ -1421,7 +1421,7 @@ mod tests {
"Content-Type".to_owned(),
b"text/html; charset=utf-8".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
preload_scan_ready_streaming_raw_chunks(
&mut runtime,
@@ -1482,7 +1482,7 @@ mod tests {
"Content-Type".to_owned(),
b"text/html; charset=utf-8".to_vec(),
)];
let mut decoder = HtmlDocumentStreamingDecoder::new(&headers);
let mut decoder = DocumentStreamingDecoder::new(&headers);
let chunks = prebootstrap_scan_ready_streaming_raw_chunks(
&mut state,
@@ -1,5 +1,5 @@
use anyhow::{Result, anyhow};
use moli_encoding::HtmlDocumentStreamingDecoder;
use moli_encoding::DocumentStreamingDecoder;
use std::time::Instant;
use super::streaming::{
@@ -21,7 +21,7 @@ use super::{
pub(in crate::runtime) struct PendingStreamingPhaseOneContinuation {
runtime: Box<ConcurrentParseTimeRuntime>,
input: StreamingDocumentInputSource,
decoder: HtmlDocumentStreamingDecoder,
decoder: DocumentStreamingDecoder,
service_worker_preload_context: Option<ServiceWorkerScriptPreloadContext>,
started: Instant,
deferred_main_resource_failure: Option<anyhow::Error>,
@@ -35,7 +35,7 @@ impl PendingStreamingPhaseOneContinuation {
pub(super) fn bridge(
runtime: ConcurrentParseTimeRuntime,
raw_body: RawDocumentBodySource,
decoder: HtmlDocumentStreamingDecoder,
decoder: DocumentStreamingDecoder,
service_worker_preload_context: Option<ServiceWorkerScriptPreloadContext>,
started: Instant,
) -> Result<Self> {