nx_layout_default_display.nx source
↩ module page · 245 lines · 11528 B
1// nx_layout_default_display.nx -- map an HTML element tag name to
2// its CSS default display kind (BLOCK / INLINE / INLINE_BLOCK).
3// Phase 3 third primitive of NISHI_BROWSER_ROADMAP.
4//
5// Per W3C HTML spec ยง 15 (default rendering / user-agent stylesheet)
6// and CSS Display Module Level 3, every HTML element has a default
7// `display` value the user-agent stylesheet sets. This primitive
8// returns that value for the subset of tags andelinwest.com + the
9// IDE require.
10//
11// The eventual nx_layout_from_dom primitive (queued) walks a parsed
12// DOM tree and calls this to assign LayoutBox.kind. Author-supplied
13// CSS can override via the cascade (Phase 2's nx_css_apply, then a
14// Phase 3b `display` property resolver).
15//
16// Subset (most common HTML5 elements):
17// BLOCK: html body div p h1 h2 h3 h4 h5 h6 header footer main
18// section article nav aside ul ol li dl dt dd table thead
19// tbody tfoot tr blockquote pre figure figcaption form
20// fieldset hr address
21// INLINE_BLOCK: img button input textarea select
22// INLINE: span a em strong b i u code small mark sub sup label
23// (and any tag not in the above two lists)
24// TEXT: handled separately by caller from text-node detection
25//
26// What it does NOT handle yet (Phase 3b improvements):
27// - SVG / MathML namespace switches
28// - <table> internal display values (table / table-row / table-cell)
29// - <script> / <style> / <head> / <title> default-hidden treatment
30// (these have `display: none` semantically; caller filters)
31// - HTML5 custom elements (no default; caller's UA stylesheet)
32// - Case-insensitive tag matching for HTML quirks mode (HTML5
33// parser lowercases tags; until nx_html_tokenizer normalizes,
34// callers pre-lowercase if needed)
35//
36// Per cardinal feedback-honest-perf-verdict-no-aspirational-claims:
37// every tag IS exhaustively listed in this header.
38//
39// genealogy_id: w3c_html5_user_agent_stylesheet_default_rendering +
40// w3c_css_display_module_level_3
41// lineage_id: nishi_browser_layout_default_display_v1
42//
43// nx_safety_envelope:
44// intended_use: "HTML tag -> default CSS display kind"
45// sil_target: SIL1
46// evidence: [W3C_HTML5_canonical_basis,
47// sealed_display_kind_enum,
48// pure_function_table_lookup]
49// verdict: NOT_YET_EVALUATED
50
51import "nx_syscalls.nx"
52import "nx_layout_box.nx"
53
54// ---- helpers ----
55
56// Lowercase ASCII single byte (A..Z -> a..z); leave others unchanged.
57func _disp_lc(c: i64) -> i64 {
58 if c >= 65 { // 'A'
59 if c <= 90 { return c + 32 }
60 }
61 return c
62}
63
64// Byte-equal compare a candidate tag (src[off..off+len]) against an
65// inline-listed literal byte sequence m0..m(N-1), case-insensitive
66// over the candidate. Helpers for each N keep the API small and
67// dodge variadic complexity.
68
69func _disp_eq2(src: *u8, off: i64, len: i64,
70 m0: i64, m1: i64) -> i64 {
71 if len != 2 { return 0 }
72 let c0: i64 = _disp_lc((src[off] as i64) & 255)
73 let c1: i64 = _disp_lc((src[off + 1] as i64) & 255)
74 if c0 != m0 { return 0 }
75 if c1 != m1 { return 0 }
76 return 1
77}
78
79func _disp_eq3(src: *u8, off: i64, len: i64,
80 m0: i64, m1: i64, m2: i64) -> i64 {
81 if len != 3 { return 0 }
82 let c0: i64 = _disp_lc((src[off] as i64) & 255)
83 let c1: i64 = _disp_lc((src[off + 1] as i64) & 255)
84 let c2: i64 = _disp_lc((src[off + 2] as i64) & 255)
85 if c0 != m0 { return 0 }
86 if c1 != m1 { return 0 }
87 if c2 != m2 { return 0 }
88 return 1
89}
90
91func _disp_eq4(src: *u8, off: i64, len: i64,
92 m0: i64, m1: i64, m2: i64, m3: i64) -> i64 {
93 if len != 4 { return 0 }
94 let c0: i64 = _disp_lc((src[off] as i64) & 255)
95 let c1: i64 = _disp_lc((src[off + 1] as i64) & 255)
96 let c2: i64 = _disp_lc((src[off + 2] as i64) & 255)
97 let c3: i64 = _disp_lc((src[off + 3] as i64) & 255)
98 if c0 != m0 { return 0 }
99 if c1 != m1 { return 0 }
100 if c2 != m2 { return 0 }
101 if c3 != m3 { return 0 }
102 return 1
103}
104
105func _disp_eq5(src: *u8, off: i64, len: i64,
106 m0: i64, m1: i64, m2: i64, m3: i64, m4: i64) -> i64 {
107 if len != 5 { return 0 }
108 let c0: i64 = _disp_lc((src[off] as i64) & 255)
109 let c1: i64 = _disp_lc((src[off + 1] as i64) & 255)
110 let c2: i64 = _disp_lc((src[off + 2] as i64) & 255)
111 let c3: i64 = _disp_lc((src[off + 3] as i64) & 255)
112 let c4: i64 = _disp_lc((src[off + 4] as i64) & 255)
113 if c0 != m0 { return 0 }
114 if c1 != m1 { return 0 }
115 if c2 != m2 { return 0 }
116 if c3 != m3 { return 0 }
117 if c4 != m4 { return 0 }
118 return 1
119}
120
121func _disp_eq6(src: *u8, off: i64, len: i64,
122 m0: i64, m1: i64, m2: i64, m3: i64, m4: i64, m5: i64) -> i64 {
123 if len != 6 { return 0 }
124 let c0: i64 = _disp_lc((src[off] as i64) & 255)
125 let c1: i64 = _disp_lc((src[off + 1] as i64) & 255)
126 let c2: i64 = _disp_lc((src[off + 2] as i64) & 255)
127 let c3: i64 = _disp_lc((src[off + 3] as i64) & 255)
128 let c4: i64 = _disp_lc((src[off + 4] as i64) & 255)
129 let c5: i64 = _disp_lc((src[off + 5] as i64) & 255)
130 if c0 != m0 { return 0 }
131 if c1 != m1 { return 0 }
132 if c2 != m2 { return 0 }
133 if c3 != m3 { return 0 }
134 if c4 != m4 { return 0 }
135 if c5 != m5 { return 0 }
136 return 1
137}
138
139func _disp_eq7(src: *u8, off: i64, len: i64,
140 m0: i64, m1: i64, m2: i64, m3: i64, m4: i64, m5: i64, m6: i64) -> i64 {
141 if len != 7 { return 0 }
142 let c0: i64 = _disp_lc((src[off] as i64) & 255)
143 let c1: i64 = _disp_lc((src[off + 1] as i64) & 255)
144 let c2: i64 = _disp_lc((src[off + 2] as i64) & 255)
145 let c3: i64 = _disp_lc((src[off + 3] as i64) & 255)
146 let c4: i64 = _disp_lc((src[off + 4] as i64) & 255)
147 let c5: i64 = _disp_lc((src[off + 5] as i64) & 255)
148 let c6: i64 = _disp_lc((src[off + 6] as i64) & 255)
149 if c0 != m0 { return 0 }
150 if c1 != m1 { return 0 }
151 if c2 != m2 { return 0 }
152 if c3 != m3 { return 0 }
153 if c4 != m4 { return 0 }
154 if c5 != m5 { return 0 }
155 if c6 != m6 { return 0 }
156 return 1
157}
158
159// Single-char tag check (h1..h6, p, b, i, u, a). Returns 1 if the
160// candidate is exactly the single byte `m`.
161func _disp_eq1(src: *u8, off: i64, len: i64, m: i64) -> i64 {
162 if len != 1 { return 0 }
163 let c: i64 = _disp_lc((src[off] as i64) & 255)
164 if c != m { return 0 }
165 return 1
166}
167
168// h1..h6 family: two chars, first is 'h', second is '1'..'6'.
169func _disp_is_h_family(src: *u8, off: i64, len: i64) -> i64 {
170 if len != 2 { return 0 }
171 let c0: i64 = _disp_lc((src[off] as i64) & 255)
172 let c1: i64 = (src[off + 1] as i64) & 255
173 if c0 != 104 { return 0 } // 'h'
174 if c1 < 49 { return 0 } // '1'
175 if c1 > 54 { return 0 } // '6'
176 return 1
177}
178
179// ---- public API ----
180
181// Return the LayoutBox kind for the given tag name. Falls back to
182// INLINE for unknown tags (matches the HTML5 user-agent default --
183// any unrecognized element is inline unless its CSS overrides).
184func nx_layout_default_display(src: *u8, tag_off: i64, tag_len: i64) -> i64 {
185 if tag_len <= 0 { return NX_LAYOUT_BOX_INLINE }
186
187 // Single-char block: p
188 if _disp_eq1(src, tag_off, tag_len, 112) == 1 { return NX_LAYOUT_BOX_BLOCK } // p
189
190 // h1..h6 family.
191 if _disp_is_h_family(src, tag_off, tag_len) == 1 { return NX_LAYOUT_BOX_BLOCK }
192
193 // 2-char blocks: hr, ul, ol, li, dl, dt, dd, tr
194 if _disp_eq2(src, tag_off, tag_len, 104, 114) == 1 { return NX_LAYOUT_BOX_BLOCK } // hr
195 if _disp_eq2(src, tag_off, tag_len, 117, 108) == 1 { return NX_LAYOUT_BOX_BLOCK } // ul
196 if _disp_eq2(src, tag_off, tag_len, 111, 108) == 1 { return NX_LAYOUT_BOX_BLOCK } // ol
197 if _disp_eq2(src, tag_off, tag_len, 108, 105) == 1 { return NX_LAYOUT_BOX_BLOCK } // li
198 if _disp_eq2(src, tag_off, tag_len, 100, 108) == 1 { return NX_LAYOUT_BOX_BLOCK } // dl
199 if _disp_eq2(src, tag_off, tag_len, 100, 116) == 1 { return NX_LAYOUT_BOX_BLOCK } // dt
200 if _disp_eq2(src, tag_off, tag_len, 100, 100) == 1 { return NX_LAYOUT_BOX_BLOCK } // dd
201 if _disp_eq2(src, tag_off, tag_len, 116, 114) == 1 { return NX_LAYOUT_BOX_BLOCK } // tr
202 if _disp_eq2(src, tag_off, tag_len, 116, 100) == 1 { return NX_LAYOUT_BOX_BLOCK } // td (table-cell: a block container, NOT inline -- else its block children flatten out)
203 if _disp_eq2(src, tag_off, tag_len, 116, 104) == 1 { return NX_LAYOUT_BOX_BLOCK } // th (table-cell)
204
205 // 3-char blocks: div, nav, pre
206 if _disp_eq3(src, tag_off, tag_len, 100, 105, 118) == 1 { return NX_LAYOUT_BOX_BLOCK } // div
207 if _disp_eq3(src, tag_off, tag_len, 110, 97, 118) == 1 { return NX_LAYOUT_BOX_BLOCK } // nav
208 if _disp_eq3(src, tag_off, tag_len, 112, 114, 101) == 1 { return NX_LAYOUT_BOX_BLOCK } // pre
209
210 // 3-char inline-block: img
211 if _disp_eq3(src, tag_off, tag_len, 105, 109, 103) == 1 { return NX_LAYOUT_BOX_INLINE_BLOCK } // img
212
213 // 4-char blocks: body, html, main, form, aside (5), table, thead/tbody/tfoot
214 if _disp_eq4(src, tag_off, tag_len, 98, 111, 100, 121) == 1 { return NX_LAYOUT_BOX_BLOCK } // body
215 if _disp_eq4(src, tag_off, tag_len, 104, 116, 109, 108) == 1 { return NX_LAYOUT_BOX_BLOCK } // html
216 if _disp_eq4(src, tag_off, tag_len, 109, 97, 105, 110) == 1 { return NX_LAYOUT_BOX_BLOCK } // main
217 if _disp_eq4(src, tag_off, tag_len, 102, 111, 114, 109) == 1 { return NX_LAYOUT_BOX_BLOCK } // form
218
219 // 5-char blocks: aside, table, tbody/thead/tfoot
220 if _disp_eq5(src, tag_off, tag_len, 97, 115, 105, 100, 101) == 1 { return NX_LAYOUT_BOX_BLOCK } // aside
221 if _disp_eq5(src, tag_off, tag_len, 116, 97, 98, 108, 101) == 1 { return NX_LAYOUT_BOX_BLOCK } // table
222 if _disp_eq5(src, tag_off, tag_len, 116, 98, 111, 100, 121) == 1 { return NX_LAYOUT_BOX_BLOCK } // tbody
223 if _disp_eq5(src, tag_off, tag_len, 116, 104, 101, 97, 100) == 1 { return NX_LAYOUT_BOX_BLOCK } // thead
224 if _disp_eq5(src, tag_off, tag_len, 116, 102, 111, 111, 116) == 1 { return NX_LAYOUT_BOX_BLOCK } // tfoot
225
226 // 5-char inline-block: input
227 if _disp_eq5(src, tag_off, tag_len, 105, 110, 112, 117, 116) == 1 { return NX_LAYOUT_BOX_INLINE_BLOCK } // input
228
229 // 6-char blocks: header, footer, figure, fieldset (8), address (7)
230 if _disp_eq6(src, tag_off, tag_len, 104, 101, 97, 100, 101, 114) == 1 { return NX_LAYOUT_BOX_BLOCK } // header
231 if _disp_eq6(src, tag_off, tag_len, 102, 111, 111, 116, 101, 114) == 1 { return NX_LAYOUT_BOX_BLOCK } // footer
232 if _disp_eq6(src, tag_off, tag_len, 102, 105, 103, 117, 114, 101) == 1 { return NX_LAYOUT_BOX_BLOCK } // figure
233 if _disp_eq6(src, tag_off, tag_len, 98, 117, 116, 116, 111, 110) == 1 { return NX_LAYOUT_BOX_INLINE_BLOCK } // button
234 if _disp_eq6(src, tag_off, tag_len, 115, 101, 108, 101, 99, 116) == 1 { return NX_LAYOUT_BOX_INLINE_BLOCK } // select
235
236 // 7-char blocks: article, section, address
237 if _disp_eq7(src, tag_off, tag_len, 97, 114, 116, 105, 99, 108, 101) == 1 { return NX_LAYOUT_BOX_BLOCK } // article
238 if _disp_eq7(src, tag_off, tag_len, 115, 101, 99, 116, 105, 111, 110) == 1 { return NX_LAYOUT_BOX_BLOCK } // section
239 if _disp_eq7(src, tag_off, tag_len, 97, 100, 100, 114, 101, 115, 115) == 1 { return NX_LAYOUT_BOX_BLOCK } // address
240
241 // Everything else (span, a, em, strong, b, i, u, code, small,
242 // mark, sub, sup, label, img-when-Phase3b, etc.) defaults to
243 // INLINE per the HTML5 user-agent stylesheet.
244 return NX_LAYOUT_BOX_INLINE
245}