Skip to content

chore(PLA-2392): do not attempt inlining for very deeploy nested html strings#11

Merged
jareddellitt merged 7 commits intomainfrom
jdellitt/PLA-2392
Mar 18, 2026
Merged

chore(PLA-2392): do not attempt inlining for very deeploy nested html strings#11
jareddellitt merged 7 commits intomainfrom
jdellitt/PLA-2392

Conversation

@jareddellitt
Copy link
Contributor

Description

Adds a fast depth check before attempting inlining and returns an error if the maximum is reached. The function doing the checking isn't doing any allocations, and is an O(n) algorithm that bails early if the max has been hit so it doesn't have to scan the whole doc. For smaller docs it will read everything, but impact should be negligible because most aren't anywhere near as long as we saw with the pathological case.

@linear
Copy link

linear bot commented Mar 18, 2026

[package]
name = "css_inline_nif"
version = "0.1.2"
version = "0.2.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bumping to next minor version because of the nature of this change

Comment on lines 3 to +20
@@ -17,4 +17,4 @@ nif_version_2_17 = ["rustler/nif_version_2_17"]

[dependencies]
rustler = "0.37"
css-inline = "0.19.1"
css-inline = "0.20.0"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a new version of css-inline, so went ahead an pulled it in. Some minor performance improvements and a bug fix.

/// since BEAM-managed data must live on the BEAM heap.
#[rustler::nif(schedule = "DirtyCpu")]
fn inline_css(html: &str, opts: Options) -> Result<Vec<u8>, RustlerError> {
if exceeds_nesting_depth(html.as_bytes(), MAX_NESTING_DEPTH) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious if we want to expose exceeds_nesting_depth/2 to Elixir and make calls to it outside of inline_css? That allows for feature flagging and perhaps dynamic control of MAX_NESTING_DEPTH?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that, but without a use case opted not to. Maybe we could just make the max depth an option that's passed in and use it if it's given, else fallback to the statically defined max?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this - make the max depth an option that's passed in:

  • unset == use the default
  • -1 == skip the check
  • Otherwise use the value passed in

Just thinking about ways to incrementally roll this out on our critical path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an option to check depth and another for the actual depth.

const MAX_NESTING_DEPTH: usize = 128;

/// Returns `true` if the DOM nesting depth exceeds `limit` via a fast byte
/// scan, without parsing. Counts `<x` as an open and `</` as a close;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: thoughts on viewing /> as close too? That helps avoid the depth limit issues around tons of e.g. <br/> tags?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overcounting from void elements is already factored into the 128 threshold. The scanner's job is to be a cheap, conservative pre-pass and not an accurate depth counter. It would technically be more accurate, but we'd increase the complexity...

Copy link

@brentjanderson brentjanderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 One nit, non-blocking.

@jareddellitt jareddellitt merged commit 2a10297 into main Mar 18, 2026
3 checks passed
@jareddellitt jareddellitt deleted the jdellitt/PLA-2392 branch March 18, 2026 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants