Skip to content
Open
18 changes: 6 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions scripts/build/deps/lolhtml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import type { Dependency } from "../source.ts";
// (`HtmlRewriter::resume()`), maintained on the `bun` branch. The upstream
// base commit is recorded here so a rebase onto a new upstream tag is
// `git rebase --onto <new-tag> <LOLHTML_UPSTREAM_BASE> bun` in the fork.
const LOLHTML_UPSTREAM_BASE = "77127cd2b8545998756e8d64e36ee2313c4bb312"; // v2.7.2
const LOLHTML_COMMIT = "725ce499aa9b71e38b7a2d0a9fbb6d7294a4079e";
const LOLHTML_UPSTREAM_BASE = "608cc4a66b7ab4fcbe1bbdeb25df8f265572b11c"; // v3.0.1
const LOLHTML_COMMIT = "e1a306d32355e264a93ab41e2580f56444971aef";
Comment thread
robobun marked this conversation as resolved.
Resolved
void LOLHTML_UPSTREAM_BASE;

export const lolhtml: Dependency = {
Expand Down
29 changes: 12 additions & 17 deletions src/bundler/HTMLScanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,14 +271,12 @@ const TAG_HANDLERS: [TagHandler; 16] = [
// TagHandler::new("iframe[src]", "src", ImportKind::Url),
];

const SELECTOR_CAP: usize = TAG_HANDLERS.len() + 3;

#[inline]
fn lol_err<E>(_: E) -> Error {
crate::Error::Fail
}

/// `element_content_handlers` entry with only the element slot populated —
/// `append_element_content_handler` entry with only the element slot populated —
/// the only shape this processor registers (leaving the comment/text slots
/// empty lets lol-html skip lexing that content).
fn element_entry<'h>(
Expand Down Expand Up @@ -310,7 +308,14 @@ impl<T: HTMLProcessorHandler, const VISIT_DOCUMENT_TAGS: bool>
// until the rewriter holding those closures is gone.
let this_ptr: *mut T = this;

let mut element_content_handlers = Vec::with_capacity(SELECTOR_CAP);
let mut settings = lol_html::Settings::new()
.with_encoding(lol_html::AsciiCompatibleEncoding::utf_8())
.with_memory_settings(
lol_html::MemorySettings::new()
.with_preallocated_parsing_buffer_size((input.len() / 4).max(1024))
.with_max_allowed_memory_usage(1024 * 1024 * 10),
Comment thread
robobun marked this conversation as resolved.
Resolved
)
.with_strict(false);

for tag_info in TAG_HANDLERS {
let on_element: lol_html::ElementHandler<'_> = Box::new(
Expand Down Expand Up @@ -339,7 +344,8 @@ impl<T: HTMLProcessorHandler, const VISIT_DOCUMENT_TAGS: bool>
Ok(())
},
);
element_content_handlers.push(element_entry(tag_info.selector, on_element)?);
settings = settings
.append_element_content_handler(element_entry(tag_info.selector, on_element)?);
}

if VISIT_DOCUMENT_TAGS {
Expand All @@ -363,21 +369,10 @@ impl<T: HTMLProcessorHandler, const VISIT_DOCUMENT_TAGS: bool>
}
},
);
element_content_handlers.push(element_entry(tag, on_element)?);
settings = settings.append_element_content_handler(element_entry(tag, on_element)?);
}
}

let settings = lol_html::Settings {
element_content_handlers,
encoding: lol_html::AsciiCompatibleEncoding::utf_8(),
memory_settings: lol_html::MemorySettings {
preallocated_parsing_buffer_size: (input.len() / 4).max(1024),
max_allowed_memory_usage: 1024 * 1024 * 10,
},
strict: false,
..lol_html::Settings::new()
};

// lol-html signals end-of-document with one zero-length chunk; the
// C-API sink routed that to a no-op `done()`, never to `on_write_html`.
let output_sink = OutputSink::Callback(Box::new(move |chunk: &[u8]| {
Expand Down
49 changes: 18 additions & 31 deletions src/runtime/api/html_rewriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,19 +293,11 @@ fn handler_result(outcome: HandlerOutcome) -> lol_html::HandlerResult {
}
}

/// Build the [`lol_html::Settings`] handler vectors from `ctx`. The lifetime
/// Build the [`lol_html::Settings`] that carry `ctx`'s handlers. The lifetime
/// erasures below are sound because the consuming sink's `Rc` keeps `ctx` alive
/// and `handler_callback` detaches each JS wrapper before its handler returns.
fn build_settings(
ctx: &mut LOLHTMLContext,
) -> (
Vec<(
std::borrow::Cow<'static, lol_html::Selector>,
lol_html::ElementContentHandlers<'static>,
)>,
Vec<lol_html::DocumentContentHandlers<'static>>,
) {
let mut element_content_handlers = Vec::with_capacity(ctx.element_handlers.len());
fn build_settings(ctx: &mut LOLHTMLContext) -> lol_html::Settings<'static, 'static> {
let mut settings = lol_html::Settings::new();
for ElementHandlerEntry { selector, handler } in &mut ctx.element_handlers {
let has_element = handler.on_element_callback.is_some();
let has_comment = handler.on_comment_callback.is_some();
Expand Down Expand Up @@ -338,10 +330,10 @@ fn build_settings(
handler_result(ElementHandler::on_text(h, raw))
});
}
element_content_handlers.push((std::borrow::Cow::Owned(selector.clone()), handlers));
settings = settings
.append_element_content_handler((std::borrow::Cow::Owned(selector.clone()), handlers));
}

let mut document_content_handlers = Vec::with_capacity(ctx.document_handlers.len());
for handler in &mut ctx.document_handlers {
let has_doc_type = handler.on_doc_type_callback.is_some();
let has_comment = handler.on_comment_callback.is_some();
Expand Down Expand Up @@ -380,10 +372,10 @@ fn build_settings(
handler_result(DocumentHandler::on_end(h, raw))
});
}
document_content_handlers.push(handlers);
settings = settings.append_document_content_handler(handlers);
}

(element_content_handlers, document_content_handlers)
settings
}

// ───────────────────────────── HTMLRewriter ──────────────────────────────
Expand Down Expand Up @@ -1062,23 +1054,18 @@ impl RewriterPipe {

// The handler closures point into `Box`es owned by `(*pipe).context`,
// which `pipe` keeps alive for the rewriter's whole lifetime.
let (element_content_handlers, document_content_handlers) =
build_settings(&mut this.context.borrow_mut());
let settings = build_settings(&mut this.context.borrow_mut())
.with_encoding(lol_html::AsciiCompatibleEncoding::utf_8())
// Default parsing-buffer preallocation: it only ever holds the
// unparsed tail of one write (a token split across chunks).
Comment thread
robobun marked this conversation as resolved.
Resolved
.with_memory_settings(
lol_html::MemorySettings::new().with_max_allowed_memory_usage(u32::MAX as usize),
)
.with_strict(false)
.with_enable_esi_tags(false)
.with_adjust_charset_on_meta_tag(false);
this.rewriter.set(Some(Box::new(lol_html::HtmlRewriter::new(
lol_html::Settings {
element_content_handlers,
document_content_handlers,
encoding: lol_html::AsciiCompatibleEncoding::utf_8(),
// Default parsing-buffer preallocation: it only ever holds the
// unparsed tail of one write (a token split across chunks).
memory_settings: lol_html::MemorySettings {
max_allowed_memory_usage: u32::MAX as usize,
..lol_html::MemorySettings::new()
},
strict: false,
enable_esi_tags: false,
adjust_charset_on_meta_tag: false,
},
settings,
// The pipe owns the `Box<LolRewriter>` that owns this sink, so the
// back-reference to `output_buffer` cannot outlive its pointee.
bun_bundler::HTMLScanner::OutputSink::Buffer(bun_ptr::BackRef::new(
Expand Down
Loading
Loading