Skip to content

BUGFIX: template-require-iframe-title — flag title={{null|undefined|number}}#16

Closed
johanrd wants to merge 2 commits intomasterfrom
fix/iframe-title-value-checks
Closed

BUGFIX: template-require-iframe-title — flag title={{null|undefined|number}}#16
johanrd wants to merge 2 commits intomasterfrom
fix/iframe-title-value-checks

Conversation

@johanrd
Copy link
Copy Markdown
Owner

@johanrd johanrd commented Apr 21, 2026

  • Premise: An <iframe> needs a non-empty string title for assistive technology. Mustache literals like {{null}}, {{undefined}}, or {{42}} produce no meaningful accessible name.
  • Problem: Our rule only rejected title={{false}}. title={{null}}, title={{undefined}}, and title={{42}} silently passed.

Fix: extract isInvalidTitleLiteral(); treat GlimmerBooleanLiteral, GlimmerNullLiteral, GlimmerUndefinedLiteral, and GlimmerNumberLiteral as invalid title values in both GlimmerMustacheStatement and single-part GlimmerConcatStatement positions.

Six new invalid tests (3 literal types × 2 syntax forms: title={{x}} vs title="{{x}}").

Whitespace-only title values (e.g. title=" ") remain flagged. The audit noted jsx-a11y accepts those, but a whitespace-only title provides no accessible name — keeping our stricter behavior.

Prior art

Plugin Rule Notes
jsx-a11y iframe-has-title Uses getPropValue; non-string literals (null/undefined/number) all fail the truthy-string check.
vuejs-accessibility iframe-has-title Same.
lit-a11y iframe-title Existence-only (`!element.attribs.title

Upstream [email protected] has the same under-flag.

Before: the rule only rejected title={{false}} among mustache literals.
title={{null}}, title={{undefined}}, and title={{42}} (any non-string
literal) were silently accepted even though none produces a useful
accessible name for an <iframe>.

Fix: extract isInvalidTitleLiteral(); treat boolean, null, undefined,
and numeric literals as invalid title values in both GlimmerMustacheStatement
and single-part GlimmerConcatStatement positions.

Six new invalid tests (3 literal types × 2 syntax forms).

Note: whitespace-only title values (e.g. title="   ") remain flagged —
the audit suggested upstream jsx-a11y accepts these, but an all-whitespace
title provides no accessible name, so our stricter behavior is kept.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 21, 2026

🏎️ Benchmark Comparison

Benchmark Control (p50) Experiment (p50) Δ
js small 14.20 ms 14.17 ms -0.2%
js medium 7.18 ms 7.07 ms -1.6%
js large 2.85 ms 2.80 ms -1.8%
gjs small 1.25 ms 1.23 ms -1.8%
gjs medium 615.47 µs 612.55 µs -0.5%
gjs large 243.43 µs 242.76 µs -0.3%
gts small 1.23 ms 1.22 ms -1.1%
gts medium 615.06 µs 613.36 µs -0.3%
gts large 244.16 µs 244.72 µs +0.2%

🟢 faster · 🔴 slower · 🟠 slightly slower · ⚪ within 2%

Full mitata output
clk: ~3.09 GHz
cpu: AMD EPYC 7763 64-Core Processor
runtime: node 24.14.1 (x64-linux)

benchmark                   avg (min … max) p75 / p99    (min … top 1%)
------------------------------------------- -------------------------------
js small (control)            17.05 ms/iter  17.62 ms █▅                   
                      (12.55 ms … 31.46 ms)  28.53 ms ███▃  ▃              
                    (  5.60 mb …  10.14 mb)   7.25 mb █████▄█▁▄▁▁▁█▁▁▁▁▄█▄▄

js small (experiment)         14.72 ms/iter  15.38 ms    █                 
                      (12.99 ms … 20.03 ms)  19.34 ms ▃█▆██▆  █            
                    (  6.21 mb …   8.29 mb)   6.83 mb ██████▄▄█▄▁█▄▁▁▄▁▁▁▁▄

                             ┌                                            ┐
                             ╷ ┌──────────┬┐                              ╷
          js small (control) ├─┤          │├──────────────────────────────┤
                             ╵ └──────────┴┘                              ╵
                              ╷ ┌──┬─┐          ╷
       js small (experiment)  ├─┤  │ ├──────────┤
                              ╵ └──┴─┘          ╵
                             └                                            ┘
                             12.55 ms           20.54 ms           28.53 ms

summary
  js small (experiment)
   1.16x faster than js small (control)

------------------------------------------- -------------------------------
js medium (control)            7.78 ms/iter   8.18 ms  █                   
                       (6.69 ms … 13.64 ms)  13.00 ms ▅█▅                  
                    (  2.93 mb …   4.72 mb)   3.53 mb ███▃▄▅▆▃▁▃▃▁▂▂▁▂▁▁▂▁▂

js medium (experiment)         7.57 ms/iter   8.05 ms  █                   
                       (6.60 ms … 12.71 ms)  12.62 ms ▃█▃                  
                    (  2.64 mb …   4.48 mb)   3.55 mb ███▅▃▇▅▃▃▁▂▁▂▂▂▁▁▁▁▁▂

                             ┌                                            ┐
                              ╷┌─────┬──┐                                 ╷
         js medium (control)  ├┤     │  ├─────────────────────────────────┤
                              ╵└─────┴──┘                                 ╵
                             ╷┌─────┬──┐                               ╷
      js medium (experiment) ├┤     │  ├───────────────────────────────┤
                             ╵└─────┴──┘                               ╵
                             └                                            ┘
                             6.60 ms            9.80 ms            13.00 ms

summary
  js medium (experiment)
   1.03x faster than js medium (control)

------------------------------------------- -------------------------------
js large (control)             3.28 ms/iter   3.18 ms   █                  
                        (2.48 ms … 9.26 ms)   6.40 ms   █                  
                    (166.65 kb …   2.72 mb)   1.43 mb ▆▇█▃▃▃▂▂▂▁▁▁▁▁▂▁▂▁▂▁▁

js large (experiment)          3.04 ms/iter   2.87 ms  █                   
                        (2.60 ms … 8.63 ms)   6.41 ms ▂█                   
                    (181.97 kb …   2.70 mb)   1.43 mb ██▃▂▂▂▁▂▁▁▂▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷  ┌─────┬                                   ╷
          js large (control) ├──┤     │───────────────────────────────────┤
                             ╵  └─────┴                                   ╵
                              ╷ ┌──┬                                      ╷
       js large (experiment)  ├─┤  │──────────────────────────────────────┤
                              ╵ └──┴                                      ╵
                             └                                            ┘
                             2.48 ms            4.45 ms             6.41 ms

summary
  js large (experiment)
   1.08x faster than js large (control)

------------------------------------------- -------------------------------
gjs small (control)            1.39 ms/iter   1.32 ms █                    
                        (1.20 ms … 6.50 ms)   5.75 ms █                    
                    (282.52 kb …   1.66 mb)   1.06 mb █▇▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs small (experiment)         1.34 ms/iter   1.26 ms █                    
                        (1.19 ms … 5.62 ms)   4.70 ms █                    
                    (322.58 kb …   2.21 mb)   1.06 mb █▅▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gjs small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬                                 ╷
      gjs small (experiment) ││─────────────────────────────────┤
                             └┴                                 ╵
                             └                                            ┘
                             1.19 ms            3.47 ms             5.75 ms

summary
  gjs small (experiment)
   1.04x faster than gjs small (control)

------------------------------------------- -------------------------------
gjs medium (control)         661.68 µs/iter 629.54 µs █                    
                      (588.20 µs … 4.91 ms)   2.96 ms █                    
                    ( 26.55 kb …   1.11 mb) 541.80 kb █▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs medium (experiment)      660.11 µs/iter 629.17 µs █                    
                      (585.07 µs … 5.24 ms)   3.01 ms █                    
                    (154.87 kb …   1.20 mb) 540.66 kb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌┬                                          ╷
        gjs medium (control) ││──────────────────────────────────────────┤
                             └┴                                          ╵
                             ┌┬                                           ╷
     gjs medium (experiment) ││───────────────────────────────────────────┤
                             └┴                                           ╵
                             └                                            ┘
                             585.07 µs           1.80 ms            3.01 ms

summary
  gjs medium (experiment)
   1x faster than gjs medium (control)

------------------------------------------- -------------------------------
gjs large (control)          264.89 µs/iter 259.59 µs  █                   
                      (234.09 µs … 4.74 ms) 395.18 µs  █                   
                    (170.91 kb …   1.00 mb) 217.10 kb ██▅▆▇▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁

gjs large (experiment)       263.53 µs/iter 258.04 µs  █                   
                      (234.30 µs … 4.49 ms) 304.12 µs  █▆▃                 
                    (170.17 kb …   1.24 mb) 216.77 kb ▅███▅▂▃▇▅▅▄▂▁▂▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌───────┬                                   ╷
         gjs large (control) ├┤       │───────────────────────────────────┤
                             ╵└───────┴                                   ╵
                             ╷┌──────┬           ╷
      gjs large (experiment) ├┤      │───────────┤
                             ╵└──────┴           ╵
                             └                                            ┘
                             234.09 µs         314.64 µs          395.18 µs

summary
  gjs large (experiment)
   1.01x faster than gjs large (control)

------------------------------------------- -------------------------------
gts small (control)            1.32 ms/iter   1.25 ms █                    
                        (1.20 ms … 6.16 ms)   4.96 ms █                    
                    (538.61 kb …   1.60 mb)   1.06 mb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts small (experiment)         1.30 ms/iter   1.24 ms █                    
                        (1.19 ms … 5.44 ms)   4.90 ms █                    
                    (164.12 kb …   1.91 mb)   1.05 mb █▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ┌─┬                                          ╷
         gts small (control) │ │──────────────────────────────────────────┤
                             └─┴                                          ╵
                             ┌┬                                          ╷
      gts small (experiment) ││──────────────────────────────────────────┤
                             └┴                                          ╵
                             └                                            ┘
                             1.19 ms            3.07 ms             4.96 ms

summary
  gts small (experiment)
   1.02x faster than gts small (control)

------------------------------------------- -------------------------------
gts medium (control)         672.06 µs/iter 627.20 µs  █                   
                      (586.55 µs … 4.94 ms)   1.35 ms ▃█                   
                    ( 97.56 kb …   1.11 mb) 541.18 kb ██▃▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

gts medium (experiment)      657.11 µs/iter 626.48 µs █▄                   
                      (587.29 µs … 5.40 ms)   1.65 ms ██                   
                    ( 74.48 kb …   1.68 mb) 540.62 kb ██▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌──┬                           ╷
        gts medium (control) ├┤  │───────────────────────────┤
                             ╵└──┴                           ╵
                             ╷┌─┬                                         ╷
     gts medium (experiment) ├┤ │─────────────────────────────────────────┤
                             ╵└─┴                                         ╵
                             └                                            ┘
                             586.55 µs           1.12 ms            1.65 ms

summary
  gts medium (experiment)
   1.02x faster than gts medium (control)

------------------------------------------- -------------------------------
gts large (control)          263.82 µs/iter 258.74 µs  █                   
                      (235.21 µs … 4.33 ms) 303.06 µs  █▃▄                 
                    (170.59 kb … 901.66 kb) 216.91 kb ▆████▂▃▆▆▄▄▃▁▁▁▁▁▁▁▁▁

gts large (experiment)       270.03 µs/iter 261.37 µs █                    
                      (236.76 µs … 4.67 ms) 576.23 µs █▄                   
                    (215.70 kb … 801.88 kb) 216.59 kb ███▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

                             ┌                                            ┐
                             ╷┌──┬    ╷
         gts large (control) ├┤  │────┤
                             ╵└──┴    ╵
                             ╷┌───┬                                       ╷
      gts large (experiment) ├┤   │───────────────────────────────────────┤
                             ╵└───┴                                       ╵
                             └                                            ┘
                             235.21 µs         405.72 µs          576.23 µs

summary
  gts large (control)
   1.02x faster than gts large (experiment)

Translates 33 cases from peer-plugin rules:
  - jsx-a11y iframe-has-title
  - vuejs-accessibility iframe-has-title
  - lit-a11y iframe-title

Fixture documents parity after this fix:
  - title={{null|undefined|number}} and concat `title="{{null}}"`
    / `title="{{42}}"` are now flagged via dynamicFalseTitle.

Remaining divergences (title={{""}} still accepted, aria-hidden /
hidden exemption, whitespace-only title over-flagged, duplicate-title
detection inherited from ember-template-lint) are annotated inline.
@johanrd
Copy link
Copy Markdown
Owner Author

johanrd commented Apr 21, 2026

Moved upstream to ember-cli#2731. See that PR.

@johanrd johanrd closed this Apr 21, 2026
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.

1 participant