leptris 1.9.216.0 → 1.9.220.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Rakefile +1 -1
- data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/version.rb +1 -1
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +60 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/TODO.max-perf/7-validate-bindings.md +14 -4
- data/vendor-src/libleptris/benchmarks/html/bench_html_parse.c +44 -0
- data/vendor-src/libleptris/src/leptris/dom/element.c +10 -2
- data/vendor-src/libleptris/src/leptris/dom/text.c +10 -0
- data/vendor-src/libleptris/src/leptris/html/html_parse.c +318 -266
- data/vendor-src/libleptris/src/leptris/xslt/xslt_exec.c +24 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ee8a29e686e3a4079398135f42f0bfc4f9362d92238863ec93dba31b5875bc0
|
|
4
|
+
data.tar.gz: 390e5a0b87b57a77818207491d5cc96ea41941bdc04c6edd57fb5942f1d39c2e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c6f747575b65cb29526fa1ac86cfe9856684345444c0c29cbb4b9c1f3850e02bf938f21d86f604adeafcc2ebf14f76b7b028bf6fdbf15d955cb04217abe94dc
|
|
7
|
+
data.tar.gz: a871ecf4f747071d6b6aca705d0bd2ef037f63f2d009bc2d35dcdeefc8f269632e1a1c596296848e8cc78268e4aa6c913d0094140e0543f387505a8f8e3f4533
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to Leptris will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.9.220.0] - 2026-09-22
|
|
9
|
+
|
|
10
|
+
### Performance — engine sync (1.9.217–1.9.220)
|
|
11
|
+
|
|
12
|
+
- Vendored libleptris 1.9.220: four perf releases — the HTML
|
|
13
|
+
parse tag-id classifier stack + in-place borrowed text runs
|
|
14
|
+
(#1218 slices 1-2), the authoritative content_len text-join
|
|
15
|
+
pass (#1285 slice 1), and borrowed string-values for
|
|
16
|
+
`value-of select="."` (#682). No binding-facing surface
|
|
17
|
+
changes; audit 342/342, suite 770/0.
|
|
18
|
+
|
|
8
19
|
## [1.9.216.0] - 2026-09-21
|
|
9
20
|
|
|
10
21
|
### Added — the plan-ABI consumer contract (#298 fully bound; engine 1.9.214–216)
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.220"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/leptris/version.rb
CHANGED
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.220] - 2026-09-21
|
|
4
|
+
|
|
5
|
+
### Performance
|
|
6
|
+
|
|
7
|
+
- in-place borrowed text runs (#1218 slice 2) (html)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [1.9.219] - 2026-09-21
|
|
12
|
+
|
|
13
|
+
### Performance
|
|
14
|
+
|
|
15
|
+
- authoritative content_len in the text-join passes (#1285 slice 1) (dom)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## [1.9.218] - 2026-09-21
|
|
20
|
+
|
|
21
|
+
### Performance
|
|
22
|
+
|
|
23
|
+
- borrowed string-value for value-of select='.' (#682) (xslt)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## [1.9.217] - 2026-09-21
|
|
28
|
+
|
|
29
|
+
### Performance
|
|
30
|
+
|
|
31
|
+
- tag-id classifier stack + single-lookup start tags (#1218) (html)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## [1.9.217] - 2026-09-21
|
|
36
|
+
|
|
37
|
+
### Added — plan-ABI additive v1 (descriptor materialization, PR #1275)
|
|
38
|
+
|
|
39
|
+
Closes #1272, #1273, #1269 — the binding-facing plan upgrades:
|
|
40
|
+
|
|
41
|
+
- **#1272 attribute-predicate rows**: child rows and attribute plans
|
|
42
|
+
accept `leptris_attr_predicate` pairs; same-wire siblings partition
|
|
43
|
+
by predicate (AND across pairs, exclusive routing only when
|
|
44
|
+
predicates are present — the #1115 multi-namespace behavior is
|
|
45
|
+
untouched).
|
|
46
|
+
- **#1273 document-order identity**: every result value carries an
|
|
47
|
+
`order_index` cumulative across rows (strictly increasing in
|
|
48
|
+
document order); `LEPTRIS_PLAN_FLAG_EMIT_ORDER_SPINE` optionally
|
|
49
|
+
appends unmatched text runs so interleavings reconstruct without
|
|
50
|
+
fragment re-parses.
|
|
51
|
+
- **#1269 in-pass types + fusion**: rows carry a `type_tag`
|
|
52
|
+
(1=int / 2=float / 3=bool) executed during the walk with soft-fail
|
|
53
|
+
to string; `leptris_plan_materialize` fuses parse→walk→free into
|
|
54
|
+
one call for the binding hot path.
|
|
55
|
+
- New accessors: `leptris_plan_value_node_kind/_order_index/_int/
|
|
56
|
+
_float/_bool`. `LEPTRIS_PLAN_ABI_VERSION` stays 1 — all additions
|
|
57
|
+
are trailing fields, aggregate initializers keep compiling.
|
|
58
|
+
|
|
59
|
+
Five new specs in `test/abi/test_descriptor.cpp` (Plan1272/1273/
|
|
60
|
+
1269a/1269b); full ctest green across all 24 CI legs.
|
|
61
|
+
|
|
62
|
+
|
|
3
63
|
## [1.9.216] - 2026-09-21
|
|
4
64
|
|
|
5
65
|
### Fixed
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Lever 7: Lane 16.6 — leptris validate CLI + bindings
|
|
2
2
|
|
|
3
|
-
Status:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Status: DONE (issue #1183). All three legs shipped and verified on
|
|
4
|
+
main:
|
|
5
|
+
|
|
6
|
+
- **CLI**: `cli/commands/validate.c` (286 lines) — RELAX NG +
|
|
7
|
+
Schematron validation, stdin/file input; registered in
|
|
8
|
+
`cli/main.c:97`, listed in the help text (`leptris validate`).
|
|
9
|
+
- **leptris-ruby**: `RelaxNG#validate/#valid?/#validate_errors/
|
|
10
|
+
#validate_report`, `Schematron#validate/#valid?`,
|
|
11
|
+
`DTD#validate/#valid?` (lib/leptris/xml/{relaxng,schematron,dtd}.rb).
|
|
12
|
+
- **leptris-py**: `RelaxNG`, `DTD`, `Schematron` exported from the
|
|
13
|
+
package root (leptris/__init__.py).
|
|
14
|
+
|
|
15
|
+
Well-formedness validation is `leptris_parse_string` itself (line:col
|
|
16
|
+
error contract); no separate wrapper required.
|
|
@@ -67,7 +67,28 @@ static size_t gen_entities(char* out, int paras) {
|
|
|
67
67
|
return len;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/* Table-of-rows shape (#1218): well-formed medium HTML — one big
|
|
71
|
+
* tbody, no error-recovery cases, matching the serialbench
|
|
72
|
+
* medium.html fixture class the issue reports 3.4-7x on. */
|
|
73
|
+
static size_t gen_table_rows(char* out, int rows) {
|
|
74
|
+
size_t len = 0;
|
|
75
|
+
len += (size_t)sprintf(out + len,
|
|
76
|
+
"<!DOCTYPE html><html lang='en'><head><meta charset='utf-8'>"
|
|
77
|
+
"<title>Report</title></head><body>"
|
|
78
|
+
"<table class='data'><thead><tr><th>Id</th><th>Name</th>"
|
|
79
|
+
"<th>Value</th></tr></thead><tbody>");
|
|
80
|
+
for (int r = 0; r < rows; r++)
|
|
81
|
+
len += (size_t)sprintf(out + len,
|
|
82
|
+
"<tr class='r%d'><td>%d</td><td>row-%d</td>"
|
|
83
|
+
"<td data-v='%d'>%d.%02d</td></tr>",
|
|
84
|
+
r & 1, r, r, r, r, r & 99);
|
|
85
|
+
len += (size_t)sprintf(out + len,
|
|
86
|
+
"</tbody></table></body></html>");
|
|
87
|
+
return len;
|
|
88
|
+
}
|
|
89
|
+
|
|
70
90
|
int main(void) {
|
|
91
|
+
const char* only = getenv("HTML_BENCH_ONLY"); /* page|entities|table */
|
|
71
92
|
size_t cap = 1u << 22;
|
|
72
93
|
char* html = (char*)malloc(cap);
|
|
73
94
|
if (!html) return 1;
|
|
@@ -80,6 +101,7 @@ int main(void) {
|
|
|
80
101
|
|
|
81
102
|
int reps = getenv("HTML_BENCH_REPS") ? atoi(getenv("HTML_BENCH_REPS")) : 9;
|
|
82
103
|
double best = 1e18;
|
|
104
|
+
if (!only || strcmp(only, "page") == 0) {
|
|
83
105
|
for (int i = 0; i < reps; i++) {
|
|
84
106
|
double t0 = benchmark_time_us();
|
|
85
107
|
LeptrisDocument d = leptris_parse_html_string(html, len, &st);
|
|
@@ -89,8 +111,10 @@ int main(void) {
|
|
|
89
111
|
}
|
|
90
112
|
printf("leptris html parse: %zu bytes, %.0f us, %.1f MB/s (best of %d)\n",
|
|
91
113
|
len, best, (double)len / best, reps);
|
|
114
|
+
}
|
|
92
115
|
|
|
93
116
|
/* Entity-laden second shape (nokogiri reference: same page). */
|
|
117
|
+
if (!only || strcmp(only, "entities") == 0) {
|
|
94
118
|
len = gen_entities(html, 20000);
|
|
95
119
|
warm = leptris_parse_html_string(html, len, &st);
|
|
96
120
|
if (!warm) { printf("entity parse failed\n"); return 1; }
|
|
@@ -106,6 +130,26 @@ int main(void) {
|
|
|
106
130
|
printf("leptris html parse (entity-laden): %zu bytes, %.0f us, "
|
|
107
131
|
"%.1f MB/s (best of %d)\n",
|
|
108
132
|
len, best, (double)len / best, reps);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Table-of-rows ~2MB shape (#1218, serialbench medium class). */
|
|
136
|
+
if (!only || strcmp(only, "table") == 0) {
|
|
137
|
+
len = gen_table_rows(html, 32000);
|
|
138
|
+
warm = leptris_parse_html_string(html, len, &st);
|
|
139
|
+
if (!warm) { printf("table parse failed\n"); return 1; }
|
|
140
|
+
leptris_document_free(warm);
|
|
141
|
+
best = 1e18;
|
|
142
|
+
for (int i = 0; i < reps; i++) {
|
|
143
|
+
double t0 = benchmark_time_us();
|
|
144
|
+
LeptrisDocument d = leptris_parse_html_string(html, len, &st);
|
|
145
|
+
double us = benchmark_time_us() - t0;
|
|
146
|
+
leptris_document_free(d);
|
|
147
|
+
if (us < best) best = us;
|
|
148
|
+
}
|
|
149
|
+
printf("leptris html parse (table-rows): %zu bytes, %.0f us, "
|
|
150
|
+
"%.1f MB/s (best of %d)\n",
|
|
151
|
+
len, best, (double)len / best, reps);
|
|
152
|
+
}
|
|
109
153
|
free(html);
|
|
110
154
|
return 0;
|
|
111
155
|
}
|
|
@@ -979,7 +979,12 @@ static size_t calculate_text_length_recursive(LeptrisNode* node) {
|
|
|
979
979
|
* text->content_len directly. */
|
|
980
980
|
const char* content = leptris_text_get_content(text);
|
|
981
981
|
if (content) {
|
|
982
|
-
|
|
982
|
+
/* #1285 slice 1: frozen (parse-created,
|
|
983
|
+
* immutable) texts carry an authoritative
|
|
984
|
+
* content_len — skip the scan. */
|
|
985
|
+
size_t clen = child->frozen ? text->content_len
|
|
986
|
+
: strlen(content);
|
|
987
|
+
len += clen;
|
|
983
988
|
}
|
|
984
989
|
} else if (child->type == LEPTRIS_NODE_TYPE_CDATA) {
|
|
985
990
|
LeptrisCDATANode* cdata = (LeptrisCDATANode*)child;
|
|
@@ -1013,7 +1018,10 @@ static void copy_text_content_recursive(LeptrisNode* node, char* result, size_t*
|
|
|
1013
1018
|
LeptrisTextNode* text = (LeptrisTextNode*)child;
|
|
1014
1019
|
const char* content = leptris_text_get_content(text);
|
|
1015
1020
|
if (content) {
|
|
1016
|
-
|
|
1021
|
+
/* #1285 slice 1: frozen texts — authoritative
|
|
1022
|
+
* content_len (mirrors the length pass). */
|
|
1023
|
+
size_t clen = child->frozen ? text->content_len
|
|
1024
|
+
: strlen(content);
|
|
1017
1025
|
memcpy(result + *offset, content, clen);
|
|
1018
1026
|
*offset += clen;
|
|
1019
1027
|
}
|
|
@@ -102,6 +102,16 @@ const char* leptris_text_get_content(LeptrisTextNode* text) {
|
|
|
102
102
|
if (!text) return NULL;
|
|
103
103
|
|
|
104
104
|
if (text->borrowed && text->pool) {
|
|
105
|
+
/* #1218 slice 2: an in-place terminated run (the parser
|
|
106
|
+
* wrote the NUL into the doc-owned input copy) is already
|
|
107
|
+
* materialized — serve it directly when it carries no
|
|
108
|
+
* entity. A run WITH '&' still takes the expansion path
|
|
109
|
+
* below (dp-borrowed nodes rely on it). */
|
|
110
|
+
if (text->content_len > 0 &&
|
|
111
|
+
text->content[text->content_len] == '\0' &&
|
|
112
|
+
memchr(text->content, '&', text->content_len) == NULL) {
|
|
113
|
+
return text->content;
|
|
114
|
+
}
|
|
105
115
|
/* Entity expansion: if the borrowed content contains '&',
|
|
106
116
|
* expand predefined XML entities (&, <, etc.) and
|
|
107
117
|
* numeric character references (A, B) into a new
|
|
@@ -2121,17 +2121,8 @@ static int h_isalnum(char c) {
|
|
|
2121
2121
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
2122
2122
|
(c >= '0' && c <= '9');
|
|
2123
2123
|
}
|
|
2124
|
-
/* Void
|
|
2125
|
-
|
|
2126
|
-
"area", "base", "basefont", "bgsound", "br", "col", "embed",
|
|
2127
|
-
"frame", "hr", "img", "input", "keygen", "link", "meta", "param",
|
|
2128
|
-
"source", "track", "wbr", NULL,
|
|
2129
|
-
};
|
|
2130
|
-
static int h_is_void(const char* name) {
|
|
2131
|
-
for (int i = 0; k_void[i]; i++)
|
|
2132
|
-
if (strcmp(name, k_void[i]) == 0) return 1;
|
|
2133
|
-
return 0;
|
|
2134
|
-
}
|
|
2124
|
+
/* Void / frameset-ok-clear classifications live in HTagInfo.
|
|
2125
|
+
* fo_void (bit0 void, bit1 frameset-ok clear) — see h_tag_infos. */
|
|
2135
2126
|
|
|
2136
2127
|
/* Raw-text elements: content runs to the case-insensitive close
|
|
2137
2128
|
* tag, no markup inside. script/style take entities verbatim. */
|
|
@@ -2197,6 +2188,10 @@ typedef struct {
|
|
|
2197
2188
|
uint8_t closes; /* bit0: h_p_closes implied-end set; bit1:
|
|
2198
2189
|
* k_p_closers — replaced the per-close-tag
|
|
2199
2190
|
* strcmp scans (#1218 profile). */
|
|
2191
|
+
uint8_t fo_void; /* bit0: void element; bit1: start tag
|
|
2192
|
+
* clears frameset-ok (13.2.5.4.4) —
|
|
2193
|
+
* replaced the k_void/k_fo_clear strcmp
|
|
2194
|
+
* scans (#1218 profile). */
|
|
2200
2195
|
} HTagInfo;
|
|
2201
2196
|
|
|
2202
2197
|
static const HTagInfo* h_tag_lookup(const char* n);
|
|
@@ -2316,6 +2311,21 @@ static char* h_decode_ex(LeptrisMemoryPool* pool, const char* s,
|
|
|
2316
2311
|
* returned length is the compacted one; the string is NUL-free). */
|
|
2317
2312
|
static char* h_decode_body(LeptrisMemoryPool* pool, const char* s,
|
|
2318
2313
|
const char* e, int whatwg, size_t* out_len) {
|
|
2314
|
+
size_t len = (size_t)(e - s);
|
|
2315
|
+
/* #1218: entity-free, NUL-free runs — the dominant case on
|
|
2316
|
+
* well-formed pages — skip the entity state machine and the
|
|
2317
|
+
* NUL-strip pass: one bump alloc + memcpy. Identical output
|
|
2318
|
+
* (decode_ex passes non-'&' bytes verbatim; the strip is a
|
|
2319
|
+
* no-op without NULs). */
|
|
2320
|
+
if (len && !memchr(s, '&', len) && !memchr(s, '\0', len)) {
|
|
2321
|
+
char* fast = (char*)leptris_pool_alloc(pool, len + 1);
|
|
2322
|
+
if (!fast) return NULL;
|
|
2323
|
+
memcpy(fast, s, len);
|
|
2324
|
+
fast[len] = 0;
|
|
2325
|
+
if (out_len) *out_len = len;
|
|
2326
|
+
return fast;
|
|
2327
|
+
}
|
|
2328
|
+
{
|
|
2319
2329
|
size_t n = 0;
|
|
2320
2330
|
char* d = h_decode_ex(pool, s, e, 0, whatwg, &n);
|
|
2321
2331
|
if (!d) return NULL;
|
|
@@ -2327,6 +2337,7 @@ static char* h_decode_body(LeptrisMemoryPool* pool, const char* s,
|
|
|
2327
2337
|
d[w] = 0;
|
|
2328
2338
|
if (out_len) *out_len = w;
|
|
2329
2339
|
return d;
|
|
2340
|
+
}
|
|
2330
2341
|
}
|
|
2331
2342
|
static size_t h_utf8_encode(uint32_t cp, char* out) {
|
|
2332
2343
|
if (cp < 0x80) { out[0] = (char)cp; return 1; }
|
|
@@ -2725,6 +2736,15 @@ typedef struct {
|
|
|
2725
2736
|
* whole child chain, which is quadratic on table/text-heavy
|
|
2726
2737
|
* pages. Slots are refreshed on push (NULL) and by hb_put. */
|
|
2727
2738
|
LeptrisNodeRef open_tail[256];
|
|
2739
|
+
/* #1218 slice 2: the doc-owned input copy — borrowed text
|
|
2740
|
+
* nodes point into it. */
|
|
2741
|
+
char* owned;
|
|
2742
|
+
/* #1218: per-slot tag id (h_tag_infos index, 255 unknown) —
|
|
2743
|
+
* the in-select / template-fence / table-context stack walks
|
|
2744
|
+
* become integer compares instead of per-element name fetch +
|
|
2745
|
+
* strcmp chains. Set at push; pops truncate depth, so the
|
|
2746
|
+
* parallel array needs no pop logic. */
|
|
2747
|
+
uint8_t open_id[256];
|
|
2728
2748
|
size_t depth;
|
|
2729
2749
|
LeptrisElement root; /* first top-level element */
|
|
2730
2750
|
LeptrisNodeRef top_head; /* top-level chain: text, comments, root */
|
|
@@ -3064,90 +3084,90 @@ static int h_is_formatting(const char* n) {
|
|
|
3064
3084
|
* no-formatting sets. */
|
|
3065
3085
|
|
|
3066
3086
|
static const HTagInfo h_tag_infos[] = {
|
|
3067
|
-
{"address", 7, 1, 1, 3},
|
|
3068
|
-
{"applet", 6, 1, 0, 0},
|
|
3069
|
-
{"area", 4, 1, 0, 0},
|
|
3070
|
-
{"article", 7, 1, 1, 3},
|
|
3071
|
-
{"aside", 5, 1, 1, 3},
|
|
3072
|
-
{"base", 4, 1, 1, 0},
|
|
3073
|
-
{"basefont", 8, 1, 1, 0},
|
|
3074
|
-
{"bgsound", 7, 1, 1, 0},
|
|
3075
|
-
{"blockquote", 10, 1, 1, 3},
|
|
3076
|
-
{"body", 4, 1, 1, 0},
|
|
3077
|
-
{"br", 2, 1, 0, 0},
|
|
3078
|
-
{"button", 6, 1, 0, 0},
|
|
3079
|
-
{"caption", 7, 1, 1, 0},
|
|
3080
|
-
{"center", 6, 1, 1, 1},
|
|
3081
|
-
{"col", 3, 1, 1, 0},
|
|
3082
|
-
{"colgroup", 8, 1, 1, 0},
|
|
3083
|
-
{"dd", 2, 1, 1, 1},
|
|
3084
|
-
{"details", 7, 1, 1, 3},
|
|
3085
|
-
{"dialog", 6, 1, 1, 3},
|
|
3086
|
-
{"dir", 3, 1, 1, 3},
|
|
3087
|
-
{"div", 3, 1, 1, 3},
|
|
3088
|
-
{"dl", 2, 1, 1, 3},
|
|
3089
|
-
{"dt", 2, 1, 1, 1},
|
|
3090
|
-
{"embed", 5, 1, 0, 0},
|
|
3091
|
-
{"fieldset", 8, 1, 1, 3},
|
|
3092
|
-
{"figcaption", 10, 1, 1, 3},
|
|
3093
|
-
{"figure", 6, 1, 1, 3},
|
|
3094
|
-
{"footer", 6, 1, 1, 3},
|
|
3095
|
-
{"form", 4, 1, 1, 3},
|
|
3096
|
-
{"frame", 5, 1, 1, 0},
|
|
3097
|
-
{"frameset", 8, 1, 1, 0},
|
|
3098
|
-
{"h1", 2, 1, 1, 3},
|
|
3099
|
-
{"h2", 2, 1, 1, 3},
|
|
3100
|
-
{"h3", 2, 1, 1, 3},
|
|
3101
|
-
{"h4", 2, 1, 1, 3},
|
|
3102
|
-
{"h5", 2, 1, 1, 3},
|
|
3103
|
-
{"h6", 2, 1, 1, 3},
|
|
3104
|
-
{"head", 4, 1, 1, 0},
|
|
3105
|
-
{"header", 6, 1, 1, 3},
|
|
3106
|
-
{"hgroup", 6, 1, 1, 3},
|
|
3107
|
-
{"hr", 2, 1, 1, 3},
|
|
3108
|
-
{"html", 4, 1, 1, 0},
|
|
3109
|
-
{"iframe", 6, 1, 0, 0},
|
|
3110
|
-
{"img", 3, 1, 0, 0},
|
|
3111
|
-
{"input", 5, 1, 0, 0},
|
|
3112
|
-
{"keygen", 6, 1, 0, 0},
|
|
3113
|
-
{"li", 2, 1, 1, 3},
|
|
3114
|
-
{"link", 4, 1, 1, 0},
|
|
3115
|
-
{"listing", 7, 1, 1, 1},
|
|
3116
|
-
{"main", 4, 1, 1, 3},
|
|
3117
|
-
{"marquee", 7, 1, 0, 0},
|
|
3118
|
-
{"menu", 4, 1, 1, 3},
|
|
3119
|
-
{"meta", 4, 1, 1, 0},
|
|
3120
|
-
{"nav", 3, 1, 1, 3},
|
|
3121
|
-
{"noembed", 7, 1, 0, 0},
|
|
3122
|
-
{"noframes", 8, 1, 1, 0},
|
|
3123
|
-
{"noscript", 8, 1, 0, 0},
|
|
3124
|
-
{"object", 6, 1, 0, 0},
|
|
3125
|
-
{"ol", 2, 1, 1, 3},
|
|
3126
|
-
{"p", 1, 1, 1, 3},
|
|
3127
|
-
{"param", 5, 1, 0, 0},
|
|
3128
|
-
{"plaintext", 9, 1, 1, 1},
|
|
3129
|
-
{"pre", 3, 1, 1, 3},
|
|
3130
|
-
{"script", 6, 1, 1, 0},
|
|
3131
|
-
{"search", 6, 1, 1, 1},
|
|
3132
|
-
{"section", 7, 1, 1, 3},
|
|
3133
|
-
{"select", 6, 1, 0, 0},
|
|
3134
|
-
{"source", 6, 1, 0, 0},
|
|
3135
|
-
{"style", 5, 1, 1, 0},
|
|
3136
|
-
{"summary", 7, 1, 1, 1},
|
|
3137
|
-
{"table", 5, 1, 1, 2},
|
|
3138
|
-
{"tbody", 5, 1, 1, 0},
|
|
3139
|
-
{"td", 2, 1, 1, 0},
|
|
3140
|
-
{"template", 8, 1, 1, 0},
|
|
3141
|
-
{"textarea", 8, 1, 1, 0},
|
|
3142
|
-
{"tfoot", 5, 1, 1, 0},
|
|
3143
|
-
{"th", 2, 1, 1, 0},
|
|
3144
|
-
{"thead", 5, 1, 1, 0},
|
|
3145
|
-
{"title", 5, 1, 1, 0},
|
|
3146
|
-
{"tr", 2, 1, 1, 0},
|
|
3147
|
-
{"track", 5, 1, 0, 0},
|
|
3148
|
-
{"ul", 2, 1, 1, 3},
|
|
3149
|
-
{"wbr", 3, 1, 0, 0},
|
|
3150
|
-
{"xmp", 3, 1, 0, 1},
|
|
3087
|
+
{"address", 7, 1, 1, 3, 0},
|
|
3088
|
+
{"applet", 6, 1, 0, 0, 2},
|
|
3089
|
+
{"area", 4, 1, 0, 0, 3},
|
|
3090
|
+
{"article", 7, 1, 1, 3, 0},
|
|
3091
|
+
{"aside", 5, 1, 1, 3, 0},
|
|
3092
|
+
{"base", 4, 1, 1, 0, 1},
|
|
3093
|
+
{"basefont", 8, 1, 1, 0, 1},
|
|
3094
|
+
{"bgsound", 7, 1, 1, 0, 1},
|
|
3095
|
+
{"blockquote", 10, 1, 1, 3, 0},
|
|
3096
|
+
{"body", 4, 1, 1, 0, 0},
|
|
3097
|
+
{"br", 2, 1, 0, 0, 3},
|
|
3098
|
+
{"button", 6, 1, 0, 0, 2},
|
|
3099
|
+
{"caption", 7, 1, 1, 0, 0},
|
|
3100
|
+
{"center", 6, 1, 1, 1, 0},
|
|
3101
|
+
{"col", 3, 1, 1, 0, 1},
|
|
3102
|
+
{"colgroup", 8, 1, 1, 0, 0},
|
|
3103
|
+
{"dd", 2, 1, 1, 1, 2},
|
|
3104
|
+
{"details", 7, 1, 1, 3, 0},
|
|
3105
|
+
{"dialog", 6, 1, 1, 3, 0},
|
|
3106
|
+
{"dir", 3, 1, 1, 3, 0},
|
|
3107
|
+
{"div", 3, 1, 1, 3, 0},
|
|
3108
|
+
{"dl", 2, 1, 1, 3, 0},
|
|
3109
|
+
{"dt", 2, 1, 1, 1, 2},
|
|
3110
|
+
{"embed", 5, 1, 0, 0, 3},
|
|
3111
|
+
{"fieldset", 8, 1, 1, 3, 0},
|
|
3112
|
+
{"figcaption", 10, 1, 1, 3, 0},
|
|
3113
|
+
{"figure", 6, 1, 1, 3, 0},
|
|
3114
|
+
{"footer", 6, 1, 1, 3, 0},
|
|
3115
|
+
{"form", 4, 1, 1, 3, 0},
|
|
3116
|
+
{"frame", 5, 1, 1, 0, 1},
|
|
3117
|
+
{"frameset", 8, 1, 1, 0, 0},
|
|
3118
|
+
{"h1", 2, 1, 1, 3, 0},
|
|
3119
|
+
{"h2", 2, 1, 1, 3, 0},
|
|
3120
|
+
{"h3", 2, 1, 1, 3, 0},
|
|
3121
|
+
{"h4", 2, 1, 1, 3, 0},
|
|
3122
|
+
{"h5", 2, 1, 1, 3, 0},
|
|
3123
|
+
{"h6", 2, 1, 1, 3, 0},
|
|
3124
|
+
{"head", 4, 1, 1, 0, 0},
|
|
3125
|
+
{"header", 6, 1, 1, 3, 0},
|
|
3126
|
+
{"hgroup", 6, 1, 1, 3, 0},
|
|
3127
|
+
{"hr", 2, 1, 1, 3, 3},
|
|
3128
|
+
{"html", 4, 1, 1, 0, 0},
|
|
3129
|
+
{"iframe", 6, 1, 0, 0, 2},
|
|
3130
|
+
{"img", 3, 1, 0, 0, 3},
|
|
3131
|
+
{"input", 5, 1, 0, 0, 3},
|
|
3132
|
+
{"keygen", 6, 1, 0, 0, 3},
|
|
3133
|
+
{"li", 2, 1, 1, 3, 2},
|
|
3134
|
+
{"link", 4, 1, 1, 0, 1},
|
|
3135
|
+
{"listing", 7, 1, 1, 1, 2},
|
|
3136
|
+
{"main", 4, 1, 1, 3, 0},
|
|
3137
|
+
{"marquee", 7, 1, 0, 0, 2},
|
|
3138
|
+
{"menu", 4, 1, 1, 3, 0},
|
|
3139
|
+
{"meta", 4, 1, 1, 0, 1},
|
|
3140
|
+
{"nav", 3, 1, 1, 3, 0},
|
|
3141
|
+
{"noembed", 7, 1, 0, 0, 2},
|
|
3142
|
+
{"noframes", 8, 1, 1, 0, 2},
|
|
3143
|
+
{"noscript", 8, 1, 0, 0, 0},
|
|
3144
|
+
{"object", 6, 1, 0, 0, 2},
|
|
3145
|
+
{"ol", 2, 1, 1, 3, 0},
|
|
3146
|
+
{"p", 1, 1, 1, 3, 0},
|
|
3147
|
+
{"param", 5, 1, 0, 0, 1},
|
|
3148
|
+
{"plaintext", 9, 1, 1, 1, 2},
|
|
3149
|
+
{"pre", 3, 1, 1, 3, 2},
|
|
3150
|
+
{"script", 6, 1, 1, 0, 0},
|
|
3151
|
+
{"search", 6, 1, 1, 1, 0},
|
|
3152
|
+
{"section", 7, 1, 1, 3, 0},
|
|
3153
|
+
{"select", 6, 1, 0, 0, 2},
|
|
3154
|
+
{"source", 6, 1, 0, 0, 1},
|
|
3155
|
+
{"style", 5, 1, 1, 0, 0},
|
|
3156
|
+
{"summary", 7, 1, 1, 1, 0},
|
|
3157
|
+
{"table", 5, 1, 1, 2, 2},
|
|
3158
|
+
{"tbody", 5, 1, 1, 0, 0},
|
|
3159
|
+
{"td", 2, 1, 1, 0, 0},
|
|
3160
|
+
{"template", 8, 1, 1, 0, 0},
|
|
3161
|
+
{"textarea", 8, 1, 1, 0, 2},
|
|
3162
|
+
{"tfoot", 5, 1, 1, 0, 0},
|
|
3163
|
+
{"th", 2, 1, 1, 0, 0},
|
|
3164
|
+
{"thead", 5, 1, 1, 0, 0},
|
|
3165
|
+
{"title", 5, 1, 1, 0, 0},
|
|
3166
|
+
{"tr", 2, 1, 1, 0, 0},
|
|
3167
|
+
{"track", 5, 1, 0, 0, 1},
|
|
3168
|
+
{"ul", 2, 1, 1, 3, 0},
|
|
3169
|
+
{"wbr", 3, 1, 0, 0, 3},
|
|
3170
|
+
{"xmp", 3, 1, 0, 1, 2},
|
|
3151
3171
|
};
|
|
3152
3172
|
|
|
3153
3173
|
/* bucket[first_char] = start index; bucket[first_char+1] = end */
|
|
@@ -3157,7 +3177,11 @@ static const uint8_t h_tag_bucket[27] = {
|
|
|
3157
3177
|
63, 70, 81, 82, 82, 83, 84, 84, 84,
|
|
3158
3178
|
};
|
|
3159
3179
|
|
|
3160
|
-
/* Resolve a (lowercased) tag name's flags; NULL when unknown.
|
|
3180
|
+
/* Resolve a (lowercased) tag name's flags; NULL when unknown.
|
|
3181
|
+
* strncmp first: it stops at n's terminator, so the n[t->len]
|
|
3182
|
+
* terminator probe below only runs when n really has t->len
|
|
3183
|
+
* matching bytes (the old n[t->len]-first form read past short
|
|
3184
|
+
* names — UB clang exploited under new inlining contexts). */
|
|
3161
3185
|
static const HTagInfo* h_tag_lookup(const char* n) {
|
|
3162
3186
|
if (!n) return NULL;
|
|
3163
3187
|
unsigned char c = (unsigned char)n[0];
|
|
@@ -3165,13 +3189,47 @@ static const HTagInfo* h_tag_lookup(const char* n) {
|
|
|
3165
3189
|
for (int i = h_tag_bucket[c - 'a'];
|
|
3166
3190
|
i < h_tag_bucket[c - 'a' + 1]; i++) {
|
|
3167
3191
|
const HTagInfo* t = &h_tag_infos[i];
|
|
3168
|
-
if (
|
|
3169
|
-
|
|
3192
|
+
if (t->name[1] == n[1] &&
|
|
3193
|
+
strncmp(n, t->name, t->len) == 0 &&
|
|
3194
|
+
(uint8_t)n[t->len] == 0)
|
|
3170
3195
|
return t;
|
|
3171
3196
|
}
|
|
3172
3197
|
return NULL;
|
|
3173
3198
|
}
|
|
3174
3199
|
|
|
3200
|
+
/* #1218: tag ids + per-id LUTs. The sampler pinned the strcmp
|
|
3201
|
+
* scans (h_is_void 18 names, h_clears_frameset_ok 26 names) and
|
|
3202
|
+
* the open-stack walks (name fetch + strcmp per element) at the
|
|
3203
|
+
* top of WHATWG self-time on table-heavy pages. ids are
|
|
3204
|
+
* h_tag_infos indexes, so one first-char bucket walk per start
|
|
3205
|
+
* tag serves every later classification of that name. */
|
|
3206
|
+
#define H_ID_UNKNOWN 255u
|
|
3207
|
+
static uint8_t h_tag_id(const char* n) {
|
|
3208
|
+
const HTagInfo* t = h_tag_lookup(n);
|
|
3209
|
+
return t ? (uint8_t)(t - h_tag_infos) : (uint8_t)H_ID_UNKNOWN;
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
static uint8_t h_id_select, h_id_template, h_id_table, h_id_tbody,
|
|
3213
|
+
h_id_thead, h_id_tfoot, h_id_tr;
|
|
3214
|
+
|
|
3215
|
+
static void h_id_luts_init(void) {
|
|
3216
|
+
static int done;
|
|
3217
|
+
if (done) return;
|
|
3218
|
+
done = 1;
|
|
3219
|
+
h_id_select = h_tag_id("select");
|
|
3220
|
+
h_id_template = h_tag_id("template");
|
|
3221
|
+
h_id_table = h_tag_id("table");
|
|
3222
|
+
h_id_tbody = h_tag_id("tbody");
|
|
3223
|
+
h_id_thead = h_tag_id("thead");
|
|
3224
|
+
h_id_tfoot = h_tag_id("tfoot");
|
|
3225
|
+
h_id_tr = h_tag_id("tr");
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
static int h_is_void(const char* name) {
|
|
3229
|
+
const HTagInfo* t = h_tag_lookup(name);
|
|
3230
|
+
return t && (t->fo_void & 1);
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3175
3233
|
static int h_is_special_ww(const char* n) {
|
|
3176
3234
|
const HTagInfo* t = h_tag_lookup(n);
|
|
3177
3235
|
return t && t->special;
|
|
@@ -3287,16 +3345,11 @@ static char* h_decode_foreign(LeptrisMemoryPool* pool, const char* s,
|
|
|
3287
3345
|
}
|
|
3288
3346
|
|
|
3289
3347
|
|
|
3290
|
-
/* 13.2.5.4.4: start tags that set frameset-ok to false
|
|
3348
|
+
/* 13.2.5.4.4: start tags that set frameset-ok to false — HTagInfo
|
|
3349
|
+
* bit (see h_tag_infos). */
|
|
3291
3350
|
static int h_clears_frameset_ok(const char* n) {
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
"applet", "marquee", "object", "table", "area", "br",
|
|
3295
|
-
"embed", "img", "keygen", "wbr", "input", "hr", "textarea",
|
|
3296
|
-
"xmp", "iframe", "noembed", "noframes", "select", NULL};
|
|
3297
|
-
for (int i = 0; yes[i]; i++)
|
|
3298
|
-
if (strcmp(n, yes[i]) == 0) return 1;
|
|
3299
|
-
return 0;
|
|
3351
|
+
const HTagInfo* t = h_tag_lookup(n);
|
|
3352
|
+
return t && (t->fo_void & 2);
|
|
3300
3353
|
}
|
|
3301
3354
|
|
|
3302
3355
|
/* 13.2.5.4.4 exception: <input> clears frameset-ok UNLESS its type
|
|
@@ -3552,22 +3605,20 @@ static int h_cur_ns(HBuilder* b) {
|
|
|
3552
3605
|
|
|
3553
3606
|
/* An open <select> swallows svg/math start tags (in-select mode
|
|
3554
3607
|
* ignores unknown start tags; the html4 entry keeps libxml2's
|
|
3555
|
-
* keep-everything shape). */
|
|
3608
|
+
* keep-everything shape). #1218: open_id integer walk. */
|
|
3556
3609
|
static int h_in_select(HBuilder* b) {
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
if (
|
|
3560
|
-
}
|
|
3610
|
+
h_id_luts_init();
|
|
3611
|
+
for (size_t i = b->depth; i > 0; i--)
|
|
3612
|
+
if (b->open_id[i - 1] == h_id_select) return 1;
|
|
3561
3613
|
return 0;
|
|
3562
3614
|
}
|
|
3563
3615
|
|
|
3564
3616
|
/* #659: index of the nearest open <template> on the stack, or
|
|
3565
3617
|
* -1. The in-template insertion rules fence on it. */
|
|
3566
3618
|
static int h_template_idx(HBuilder* b) {
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
if (
|
|
3570
|
-
}
|
|
3619
|
+
h_id_luts_init();
|
|
3620
|
+
for (size_t i = b->depth; i > 0; i--)
|
|
3621
|
+
if (b->open_id[i - 1] == h_id_template) return (int)(i - 1);
|
|
3571
3622
|
return -1;
|
|
3572
3623
|
}
|
|
3573
3624
|
|
|
@@ -3713,6 +3764,15 @@ static int h_is_table_context(LeptrisElement e) {
|
|
|
3713
3764
|
h_ieq_raw(n, "tr");
|
|
3714
3765
|
}
|
|
3715
3766
|
|
|
3767
|
+
/* #1218: stack-slot flavor — integer compare on the push-time id
|
|
3768
|
+
* (identical membership: ids were resolved from the same stored
|
|
3769
|
+
* lowercase names). */
|
|
3770
|
+
static int h_id_table_ctx(uint8_t id) {
|
|
3771
|
+
h_id_luts_init();
|
|
3772
|
+
return id == h_id_table || id == h_id_tbody ||
|
|
3773
|
+
id == h_id_thead || id == h_id_tfoot || id == h_id_tr;
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3716
3776
|
/* Insert n into parent's child chain BEFORE `before`. Same surgery
|
|
3717
3777
|
* pattern as the head-content lift (first_child + sibling links +
|
|
3718
3778
|
* element child_count). */
|
|
@@ -3944,7 +4004,7 @@ static void h_append(HBuilder* b, LeptrisNodeRef n) {
|
|
|
3944
4004
|
* table" insertion mode - ordinary content still
|
|
3945
4005
|
* fosters, hidden inputs insert at spot
|
|
3946
4006
|
* (html5test-com:20). */
|
|
3947
|
-
int tbl_ctx =
|
|
4007
|
+
int tbl_ctx = h_id_table_ctx(b->open_id[b->depth - 1]);
|
|
3948
4008
|
if (!tbl_ctx && b->depth >= 2 &&
|
|
3949
4009
|
b->open_ns[b->depth - 1] == H_NS_HTML &&
|
|
3950
4010
|
h_ieq_raw(leptris_element_name(top), "form") &&
|
|
@@ -3969,7 +4029,7 @@ static void h_append(HBuilder* b, LeptrisNodeRef n) {
|
|
|
3969
4029
|
int oi = -1;
|
|
3970
4030
|
for (int k = (int)b->depth - 1; k >= 0; k--)
|
|
3971
4031
|
if (b->open_ns[k] == H_NS_HTML &&
|
|
3972
|
-
|
|
4032
|
+
h_id_table_ctx(b->open_id[k])) {
|
|
3973
4033
|
oi = k;
|
|
3974
4034
|
}
|
|
3975
4035
|
if (oi >= 0) {
|
|
@@ -4102,12 +4162,16 @@ static LeptrisElement h_open_foreign(HBuilder* b, const char* name,
|
|
|
4102
4162
|
b->open[b->depth] = e;
|
|
4103
4163
|
b->open_tail[b->depth] = NULL;
|
|
4104
4164
|
b->open_ns[b->depth] = (uint8_t)ns;
|
|
4165
|
+
b->open_id[b->depth] = h_tag_id(store);
|
|
4105
4166
|
b->depth++;
|
|
4106
4167
|
}
|
|
4107
4168
|
return e;
|
|
4108
4169
|
}
|
|
4109
4170
|
|
|
4110
|
-
|
|
4171
|
+
/* Hot-path variant (#1218): the start-tag section resolves the
|
|
4172
|
+
* HTagInfo once per token and passes the id down. */
|
|
4173
|
+
static LeptrisElement h_open_element_id(HBuilder* b, const char* name,
|
|
4174
|
+
uint8_t tid) {
|
|
4111
4175
|
LeptrisStringView nv = leptris_sv_from_cstr(name);
|
|
4112
4176
|
LeptrisElement e = leptris_element_create_with_view(nv, b->pool);
|
|
4113
4177
|
if (!e) return NULL;
|
|
@@ -4140,11 +4204,16 @@ static LeptrisElement h_open_element(HBuilder* b, const char* name) {
|
|
|
4140
4204
|
b->open[b->depth] = e;
|
|
4141
4205
|
b->open_tail[b->depth] = NULL;
|
|
4142
4206
|
b->open_ns[b->depth] = H_NS_HTML;
|
|
4207
|
+
b->open_id[b->depth] = tid;
|
|
4143
4208
|
b->depth++;
|
|
4144
4209
|
}
|
|
4145
4210
|
return e;
|
|
4146
4211
|
}
|
|
4147
4212
|
|
|
4213
|
+
static LeptrisElement h_open_element(HBuilder* b, const char* name) {
|
|
4214
|
+
return h_open_element_id(b, name, h_tag_id(name));
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4148
4217
|
/* Case-insensitive C-string compare (ASCII). */
|
|
4149
4218
|
static int h_ieq_raw(const char* a, const char* bname) {
|
|
4150
4219
|
if (!a) return 0;
|
|
@@ -4341,6 +4410,7 @@ static LeptrisElement h_afe_open_clone(HBuilder* b, LeptrisElement src) {
|
|
|
4341
4410
|
b->open[b->depth] = c;
|
|
4342
4411
|
b->open_tail[b->depth] = NULL;
|
|
4343
4412
|
b->open_ns[b->depth] = H_NS_HTML;
|
|
4413
|
+
b->open_id[b->depth] = h_tag_id(leptris_element_name(c));
|
|
4344
4414
|
b->depth++;
|
|
4345
4415
|
}
|
|
4346
4416
|
return c;
|
|
@@ -4624,6 +4694,7 @@ static LeptrisElement h_open_named(HBuilder* b, const char* name,
|
|
|
4624
4694
|
b->open[b->depth] = e;
|
|
4625
4695
|
b->open_tail[b->depth] = NULL;
|
|
4626
4696
|
b->open_ns[b->depth] = H_NS_HTML;
|
|
4697
|
+
b->open_id[b->depth] = h_tag_id(name);
|
|
4627
4698
|
b->depth++;
|
|
4628
4699
|
}
|
|
4629
4700
|
return e;
|
|
@@ -5303,6 +5374,43 @@ head_end = leptris_node_get_next_sibling(head_end);
|
|
|
5303
5374
|
}
|
|
5304
5375
|
|
|
5305
5376
|
/* ---- tokenizer ---- */
|
|
5377
|
+
/* #1218 slice 2: build a text node for the run [s, e). Clean runs
|
|
5378
|
+
* (no '&', no NUL) borrow the doc-owned input copy in place — the
|
|
5379
|
+
* terminator byte (the '<' the dispatcher already consumed, or the
|
|
5380
|
+
* buffer's own EOF NUL) becomes the content terminator: zero
|
|
5381
|
+
* copies. Entity- or NUL-bearing runs take the decode path. */
|
|
5382
|
+
static LeptrisTextNode* h_text_node(HBuilder* b, const char* s,
|
|
5383
|
+
const char* e) {
|
|
5384
|
+
size_t len = (size_t)(e - s);
|
|
5385
|
+
if (b->owned && len && !memchr(s, '&', len) &&
|
|
5386
|
+
!memchr(s, '\0', len)) {
|
|
5387
|
+
/* 13.2.6.4.7: non-whitespace text clears frameset-ok —
|
|
5388
|
+
* the decode path scans the decoded string; the borrow
|
|
5389
|
+
* path scans the raw run (identical for entity-free
|
|
5390
|
+
* runs; FFFD in body counts as non-ws, :21). */
|
|
5391
|
+
if (b->whatwg && b->frameset_ok) {
|
|
5392
|
+
for (const char* c = s; c < e; c++) {
|
|
5393
|
+
if ((unsigned char)c[0] == 0xEF && c + 2 < e &&
|
|
5394
|
+
(unsigned char)c[1] == 0xBF &&
|
|
5395
|
+
(unsigned char)c[2] == 0xBD) {
|
|
5396
|
+
b->frameset_ok = 0;
|
|
5397
|
+
break;
|
|
5398
|
+
}
|
|
5399
|
+
if (!h_is_ws(*c)) {
|
|
5400
|
+
b->frameset_ok = 0;
|
|
5401
|
+
break;
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
*(char*)e = '\0';
|
|
5406
|
+
return leptris_text_create_borrowed(s, len, b->pool);
|
|
5407
|
+
}
|
|
5408
|
+
size_t dlen = 0;
|
|
5409
|
+
char* dec = h_decode_text(b, s, e, &dlen);
|
|
5410
|
+
if (!dec || !*dec) return NULL;
|
|
5411
|
+
return leptris_text_create(dec, dlen, b->pool);
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5306
5414
|
static LeptrisDocument html_parse_shared(
|
|
5307
5415
|
const char* buf, size_t len, LeptrisStatus* status, int whatwg) {
|
|
5308
5416
|
if (status) *status = LEPTRIS_OK;
|
|
@@ -5325,9 +5433,23 @@ static LeptrisDocument html_parse_shared(
|
|
|
5325
5433
|
b.whatwg_head_set = whatwg;
|
|
5326
5434
|
b.whatwg_foster = whatwg;
|
|
5327
5435
|
b.whatwg_adopt = whatwg;
|
|
5436
|
+
/* #1218 slice 2: one copy of the input into document memory.
|
|
5437
|
+
* Clean text runs borrow this copy in place (the terminator
|
|
5438
|
+
* byte becomes their NUL) instead of a pool alloc + memcpy
|
|
5439
|
+
* per text node — the pugi-style in-place model. */
|
|
5440
|
+
char* owned = (char*)leptris_pool_alloc(b.pool, len + 1);
|
|
5441
|
+
if (!owned) {
|
|
5442
|
+
if (status) *status = LEPTRIS_ERROR_MEMORY;
|
|
5443
|
+
leptris_document_free(doc);
|
|
5444
|
+
return NULL;
|
|
5445
|
+
}
|
|
5446
|
+
memcpy(owned, buf, len);
|
|
5447
|
+
owned[len] = '\0';
|
|
5448
|
+
b.owned = owned;
|
|
5449
|
+
h_id_luts_init();
|
|
5328
5450
|
|
|
5329
|
-
const char* p =
|
|
5330
|
-
const char* end =
|
|
5451
|
+
const char* p = owned;
|
|
5452
|
+
const char* end = owned + len;
|
|
5331
5453
|
const char* text = p; /* pending text run start */
|
|
5332
5454
|
|
|
5333
5455
|
while (p < end) {
|
|
@@ -5345,14 +5467,10 @@ static LeptrisDocument html_parse_shared(
|
|
|
5345
5467
|
/* Flush pending text first. */
|
|
5346
5468
|
if (text < p) {
|
|
5347
5469
|
size_t dlen = 0;
|
|
5348
|
-
|
|
5349
|
-
if (
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
if (t) {
|
|
5353
|
-
h_reconstruct(&b);
|
|
5354
|
-
h_append(&b, (LeptrisNodeRef)t);
|
|
5355
|
-
}
|
|
5470
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
5471
|
+
if (t) {
|
|
5472
|
+
h_reconstruct(&b);
|
|
5473
|
+
h_append(&b, (LeptrisNodeRef)t);
|
|
5356
5474
|
}
|
|
5357
5475
|
}
|
|
5358
5476
|
if (p + 3 < end && p[2] == '-' && p[3] == '-') {
|
|
@@ -5677,13 +5795,9 @@ static LeptrisDocument html_parse_shared(
|
|
|
5677
5795
|
const char* ns2 = p + 2;
|
|
5678
5796
|
if (b.whatwg && ns2 >= end) {
|
|
5679
5797
|
if (text < p) {
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
if (
|
|
5683
|
-
LeptrisTextNode* t = leptris_text_create(
|
|
5684
|
-
dec, dlen, b.pool);
|
|
5685
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
5686
|
-
}
|
|
5798
|
+
LeptrisTextNode* t =
|
|
5799
|
+
h_text_node(&b, text, p);
|
|
5800
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
5687
5801
|
}
|
|
5688
5802
|
LeptrisTextNode* t =
|
|
5689
5803
|
leptris_text_create("</", 2, b.pool);
|
|
@@ -5695,13 +5809,9 @@ static LeptrisDocument html_parse_shared(
|
|
|
5695
5809
|
if (b.whatwg && !((*ns2 >= 'a' && *ns2 <= 'z') ||
|
|
5696
5810
|
(*ns2 >= 'A' && *ns2 <= 'Z'))) {
|
|
5697
5811
|
if (text < p) {
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
if (
|
|
5701
|
-
LeptrisTextNode* t = leptris_text_create(
|
|
5702
|
-
dec, dlen, b.pool);
|
|
5703
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
5704
|
-
}
|
|
5812
|
+
LeptrisTextNode* t =
|
|
5813
|
+
h_text_node(&b, text, p);
|
|
5814
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
5705
5815
|
}
|
|
5706
5816
|
const char* q2 = ns2;
|
|
5707
5817
|
while (q2 < end && *q2 != '>') q2++;
|
|
@@ -5715,13 +5825,8 @@ static LeptrisDocument html_parse_shared(
|
|
|
5715
5825
|
* dropped too (same 13.2.5.44 rule). */
|
|
5716
5826
|
if (b.whatwg && !memchr(p, '>', (size_t)(end - p))) {
|
|
5717
5827
|
if (text < p) {
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
if (dec && *dec) {
|
|
5721
|
-
LeptrisTextNode* t =
|
|
5722
|
-
leptris_text_create(dec, dlen, b.pool);
|
|
5723
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
5724
|
-
}
|
|
5828
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
5829
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
5725
5830
|
}
|
|
5726
5831
|
p = end;
|
|
5727
5832
|
text = p;
|
|
@@ -5746,26 +5851,22 @@ static LeptrisDocument html_parse_shared(
|
|
|
5746
5851
|
* whitespace-only run in a table context skips the
|
|
5747
5852
|
* reconstruct (13.2.6.4.9 - tricky01:6). */
|
|
5748
5853
|
if (text < p) {
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5854
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
5855
|
+
if (t) {
|
|
5856
|
+
const char* c = leptris_text_get_content(t);
|
|
5752
5857
|
int tbl_ws = b.whatwg_foster &&
|
|
5753
5858
|
b.depth > 0 &&
|
|
5754
|
-
|
|
5755
|
-
b.
|
|
5859
|
+
h_id_table_ctx(
|
|
5860
|
+
b.open_id[b.depth - 1]);
|
|
5756
5861
|
if (tbl_ws)
|
|
5757
|
-
for (const char* w2 =
|
|
5862
|
+
for (const char* w2 = c; *w2; w2++)
|
|
5758
5863
|
if (*w2 != ' ' && *w2 != '\t' &&
|
|
5759
5864
|
*w2 != '\n' && *w2 != '\r') {
|
|
5760
5865
|
tbl_ws = 0;
|
|
5761
5866
|
break;
|
|
5762
5867
|
}
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
if (t) {
|
|
5766
|
-
if (!tbl_ws) h_reconstruct(&b);
|
|
5767
|
-
h_append(&b, (LeptrisNodeRef)t);
|
|
5768
|
-
}
|
|
5868
|
+
if (!tbl_ws) h_reconstruct(&b);
|
|
5869
|
+
h_append(&b, (LeptrisNodeRef)t);
|
|
5769
5870
|
}
|
|
5770
5871
|
}
|
|
5771
5872
|
/* #659 tests26:17-20 - an HTML end tag with the foreign
|
|
@@ -6106,17 +6207,11 @@ static LeptrisDocument html_parse_shared(
|
|
|
6106
6207
|
if (!head_found2 && !b.head_tag_seen &&
|
|
6107
6208
|
!b.body_seen && !b.frameset) {
|
|
6108
6209
|
if (text < p) {
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
leptris_text_create(
|
|
6115
|
-
dec, dlen, b.pool);
|
|
6116
|
-
if (t)
|
|
6117
|
-
h_append(&b,
|
|
6118
|
-
(LeptrisNodeRef)t);
|
|
6119
|
-
}
|
|
6210
|
+
LeptrisTextNode* t =
|
|
6211
|
+
h_text_node(&b, text, p);
|
|
6212
|
+
if (t)
|
|
6213
|
+
h_append(&b,
|
|
6214
|
+
(LeptrisNodeRef)t);
|
|
6120
6215
|
}
|
|
6121
6216
|
b.head_tag_seen = 1;
|
|
6122
6217
|
b.head_end_seen = 1;
|
|
@@ -6446,14 +6541,10 @@ static LeptrisDocument html_parse_shared(
|
|
|
6446
6541
|
* trailing '?' — content runs to the first '>'. */
|
|
6447
6542
|
if (text < p) {
|
|
6448
6543
|
size_t dlen = 0;
|
|
6449
|
-
|
|
6450
|
-
if (
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
if (t) {
|
|
6454
|
-
h_reconstruct(&b);
|
|
6455
|
-
h_append(&b, (LeptrisNodeRef)t);
|
|
6456
|
-
}
|
|
6544
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
6545
|
+
if (t) {
|
|
6546
|
+
h_reconstruct(&b);
|
|
6547
|
+
h_append(&b, (LeptrisNodeRef)t);
|
|
6457
6548
|
}
|
|
6458
6549
|
}
|
|
6459
6550
|
const char* ts = p + 2; /* skip "<?" */
|
|
@@ -6531,13 +6622,8 @@ static LeptrisDocument html_parse_shared(
|
|
|
6531
6622
|
* parsing ends (webkit01:4: '<di' -> empty body). */
|
|
6532
6623
|
if (b.whatwg && !memchr(p, '>', (size_t)(end - p))) {
|
|
6533
6624
|
if (text < p) {
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
if (dec && *dec) {
|
|
6537
|
-
LeptrisTextNode* t =
|
|
6538
|
-
leptris_text_create(dec, dlen, b.pool);
|
|
6539
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6540
|
-
}
|
|
6625
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
6626
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6541
6627
|
}
|
|
6542
6628
|
p = end;
|
|
6543
6629
|
text = p;
|
|
@@ -6576,13 +6662,9 @@ static LeptrisDocument html_parse_shared(
|
|
|
6576
6662
|
}
|
|
6577
6663
|
if (!closed) {
|
|
6578
6664
|
if (text < p) {
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
if (
|
|
6582
|
-
LeptrisTextNode* t = leptris_text_create(
|
|
6583
|
-
dec, dlen, b.pool);
|
|
6584
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6585
|
-
}
|
|
6665
|
+
LeptrisTextNode* t =
|
|
6666
|
+
h_text_node(&b, text, p);
|
|
6667
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6586
6668
|
}
|
|
6587
6669
|
p = end;
|
|
6588
6670
|
text = p;
|
|
@@ -6598,12 +6680,19 @@ static LeptrisDocument html_parse_shared(
|
|
|
6598
6680
|
* reprocessed (tests1:90). */
|
|
6599
6681
|
if (b.whatwg && strcmp(name, "image") == 0)
|
|
6600
6682
|
name = h_pooled_lower(b.pool, "img", 3);
|
|
6683
|
+
/* #1218: ONE classification per start tag — every
|
|
6684
|
+
* downstream consumer (frameset-ok, void, open) reads the
|
|
6685
|
+
* resolved entry instead of re-walking the bucket. */
|
|
6686
|
+
const HTagInfo* tinfo = h_tag_lookup(name);
|
|
6687
|
+
uint8_t tid = tinfo ? (uint8_t)(tinfo - h_tag_infos)
|
|
6688
|
+
: (uint8_t)H_ID_UNKNOWN;
|
|
6601
6689
|
/* 13.2.5.4.4: the enumerated start tags clear frameset-ok;
|
|
6602
6690
|
* <input type=hidden> does not (webkit01:51). */
|
|
6603
6691
|
b.input_hidden_tag =
|
|
6604
6692
|
b.whatwg && nlen == 5 && strcmp(name, "input") == 0 &&
|
|
6605
6693
|
h_input_type_hidden(q, end);
|
|
6606
|
-
if (b.whatwg && b.frameset_ok &&
|
|
6694
|
+
if (b.whatwg && b.frameset_ok && tinfo &&
|
|
6695
|
+
(tinfo->fo_void & 2) &&
|
|
6607
6696
|
!(nlen == 5 && strcmp(name, "input") == 0 &&
|
|
6608
6697
|
h_input_type_hidden(q, end))) {
|
|
6609
6698
|
b.frameset_ok = 0;
|
|
@@ -6676,13 +6765,8 @@ static LeptrisDocument html_parse_shared(
|
|
|
6676
6765
|
* after-</head> whitespace must land on the chain to
|
|
6677
6766
|
* become an html child (webkit01:35). */
|
|
6678
6767
|
if (text < p) {
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
if (dec && *dec) {
|
|
6682
|
-
LeptrisTextNode* t =
|
|
6683
|
-
leptris_text_create(dec, dlen, b.pool);
|
|
6684
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6685
|
-
}
|
|
6768
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
6769
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6686
6770
|
}
|
|
6687
6771
|
if (strcmp(name, "body") == 0) {
|
|
6688
6772
|
b.body_tag_seen = 1;
|
|
@@ -6750,13 +6834,9 @@ static LeptrisDocument html_parse_shared(
|
|
|
6750
6834
|
} else if (!(b.frameset && b.depth > 0)) {
|
|
6751
6835
|
/* Dropped token: flush pending text first. */
|
|
6752
6836
|
if (text < p) {
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
if (
|
|
6756
|
-
LeptrisTextNode* t = leptris_text_create(
|
|
6757
|
-
dec, dlen, b.pool);
|
|
6758
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6759
|
-
}
|
|
6837
|
+
LeptrisTextNode* t =
|
|
6838
|
+
h_text_node(&b, text, p);
|
|
6839
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6760
6840
|
}
|
|
6761
6841
|
while (q < end && *q != '>') q++;
|
|
6762
6842
|
p = (q < end) ? q + 1 : end;
|
|
@@ -6782,13 +6862,8 @@ static LeptrisDocument html_parse_shared(
|
|
|
6782
6862
|
* (html5lib template.dat:64-67 — attrs do NOT merge
|
|
6783
6863
|
* onto the outer elements). */
|
|
6784
6864
|
if (text < p) {
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
if (dec && *dec) {
|
|
6788
|
-
LeptrisTextNode* t = leptris_text_create(
|
|
6789
|
-
dec, dlen, b.pool);
|
|
6790
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6791
|
-
}
|
|
6865
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
6866
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
6792
6867
|
}
|
|
6793
6868
|
while (q < end && *q != '>') q++;
|
|
6794
6869
|
p = (q < end) ? q + 1 : end;
|
|
@@ -6808,21 +6883,16 @@ static LeptrisDocument html_parse_shared(
|
|
|
6808
6883
|
strcmp(name, "noframes") != 0 &&
|
|
6809
6884
|
strcmp(name, "html") != 0) {
|
|
6810
6885
|
if (text < p) {
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6886
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
6887
|
+
if (t) {
|
|
6888
|
+
const char* c = leptris_text_get_content(t);
|
|
6814
6889
|
int ws = 1;
|
|
6815
|
-
for (const char* q2 =
|
|
6890
|
+
for (const char* q2 = c; *q2; q2++)
|
|
6816
6891
|
if (!h_is_ws(*q2)) {
|
|
6817
6892
|
ws = 0;
|
|
6818
6893
|
break;
|
|
6819
6894
|
}
|
|
6820
|
-
if (ws)
|
|
6821
|
-
LeptrisTextNode* t = leptris_text_create(
|
|
6822
|
-
dec, dlen, b.pool);
|
|
6823
|
-
if (t)
|
|
6824
|
-
h_append(&b, (LeptrisNodeRef)t);
|
|
6825
|
-
}
|
|
6895
|
+
if (ws) h_append(&b, (LeptrisNodeRef)t);
|
|
6826
6896
|
}
|
|
6827
6897
|
}
|
|
6828
6898
|
while (q < end && *q != '>') q++;
|
|
@@ -6833,31 +6903,27 @@ static LeptrisDocument html_parse_shared(
|
|
|
6833
6903
|
|
|
6834
6904
|
/* Flush pending text before the element. */
|
|
6835
6905
|
if (text < p) {
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
if (dec && *dec) {
|
|
6906
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
6907
|
+
if (t) {
|
|
6839
6908
|
/* 13.2.6.4.9: a WHITESPACE-ONLY run in a table
|
|
6840
6909
|
* context inserts into the current node WITHOUT
|
|
6841
6910
|
* the in-body reconstruct - no formatting clone
|
|
6842
6911
|
* wraps it (tricky01:6: the ws between
|
|
6843
6912
|
* </center> and <img> goes into the table). */
|
|
6913
|
+
const char* c = leptris_text_get_content(t);
|
|
6844
6914
|
int tbl_ws = b.whatwg_foster && b.depth > 0 &&
|
|
6845
|
-
|
|
6846
|
-
b.
|
|
6915
|
+
h_id_table_ctx(
|
|
6916
|
+
b.open_id[b.depth - 1]);
|
|
6847
6917
|
if (tbl_ws) {
|
|
6848
|
-
for (const char* w2 =
|
|
6918
|
+
for (const char* w2 = c; *w2; w2++)
|
|
6849
6919
|
if (*w2 != ' ' && *w2 != '\t' &&
|
|
6850
6920
|
*w2 != '\n' && *w2 != '\r') {
|
|
6851
6921
|
tbl_ws = 0;
|
|
6852
6922
|
break;
|
|
6853
6923
|
}
|
|
6854
6924
|
}
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
if (t) {
|
|
6858
|
-
if (!tbl_ws) h_reconstruct(&b);
|
|
6859
|
-
h_append(&b, (LeptrisNodeRef)t);
|
|
6860
|
-
}
|
|
6925
|
+
if (!tbl_ws) h_reconstruct(&b);
|
|
6926
|
+
h_append(&b, (LeptrisNodeRef)t);
|
|
6861
6927
|
}
|
|
6862
6928
|
}
|
|
6863
6929
|
|
|
@@ -7015,13 +7081,8 @@ static LeptrisDocument html_parse_shared(
|
|
|
7015
7081
|
* template.dat:64-67) and in-frameset. */
|
|
7016
7082
|
if (b.whatwg && strcmp(name, "html") == 0 && b.html_seen) {
|
|
7017
7083
|
if (text < p) {
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
if (dec && *dec) {
|
|
7021
|
-
LeptrisTextNode* t = leptris_text_create(dec, dlen,
|
|
7022
|
-
b.pool);
|
|
7023
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
7024
|
-
}
|
|
7084
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
7085
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
7025
7086
|
}
|
|
7026
7087
|
h_stash_attrs(&b, q, end, b.html_attrs, &b.html_attr_n);
|
|
7027
7088
|
while (q < end && *q != '>') q++;
|
|
@@ -7367,13 +7428,8 @@ static LeptrisDocument html_parse_shared(
|
|
|
7367
7428
|
}
|
|
7368
7429
|
if (tmpl_act == 1) {
|
|
7369
7430
|
if (text < p) {
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
if (dec && *dec) {
|
|
7373
|
-
LeptrisTextNode* t = leptris_text_create(
|
|
7374
|
-
dec, dlen, b.pool);
|
|
7375
|
-
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
7376
|
-
}
|
|
7431
|
+
LeptrisTextNode* t = h_text_node(&b, text, p);
|
|
7432
|
+
if (t) h_append(&b, (LeptrisNodeRef)t);
|
|
7377
7433
|
}
|
|
7378
7434
|
while (q < end && *q != '>') q++;
|
|
7379
7435
|
p = (q < end) ? q + 1 : end;
|
|
@@ -7640,7 +7696,7 @@ static LeptrisDocument html_parse_shared(
|
|
|
7640
7696
|
|
|
7641
7697
|
LeptrisElement e = (elem_ns != H_NS_HTML)
|
|
7642
7698
|
? h_open_foreign(&b, name, elem_ns)
|
|
7643
|
-
:
|
|
7699
|
+
: h_open_element_id(&b, name, tid);
|
|
7644
7700
|
if (!e) goto done;
|
|
7645
7701
|
if (strcmp(name, "html") == 0) {
|
|
7646
7702
|
b.html_seen = 1;
|
|
@@ -7867,7 +7923,7 @@ static LeptrisDocument html_parse_shared(
|
|
|
7867
7923
|
* their entry; applet/object/marquee/td/th/caption open a
|
|
7868
7924
|
* marker scope (13.2.4.3). */
|
|
7869
7925
|
if (b.whatwg_adopt && elem_ns == H_NS_HTML &&
|
|
7870
|
-
!(self_closing ||
|
|
7926
|
+
!(self_closing || (tinfo && (tinfo->fo_void & 1)))) {
|
|
7871
7927
|
if (h_is_formatting(name)) {
|
|
7872
7928
|
h_afe_push(&b, e);
|
|
7873
7929
|
} else if (strcmp(name, "applet") == 0 ||
|
|
@@ -7884,7 +7940,7 @@ static LeptrisDocument html_parse_shared(
|
|
|
7884
7940
|
* HTML elements (parse error, div stays open -
|
|
7885
7941
|
* webkit01:46). Only foreign self-closing elements pop.
|
|
7886
7942
|
* html4 keeps libxml2's honored-slash shape. */
|
|
7887
|
-
if (
|
|
7943
|
+
if ((tinfo && (tinfo->fo_void & 1)) ||
|
|
7888
7944
|
(self_closing &&
|
|
7889
7945
|
(elem_ns != H_NS_HTML || !b.whatwg)))
|
|
7890
7946
|
b.depth--;
|
|
@@ -7939,16 +7995,16 @@ static LeptrisDocument html_parse_shared(
|
|
|
7939
7995
|
|
|
7940
7996
|
/* Trailing text. */
|
|
7941
7997
|
if (text < end) {
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
if (dec && *dec) {
|
|
7998
|
+
LeptrisTextNode* t = h_text_node(&b, text, end);
|
|
7999
|
+
if (t) {
|
|
7945
8000
|
/* #659 after </html> with a frameset body, non-ws text
|
|
7946
8001
|
* reprocesses into the frameset and drops (13.2.6.4.18,
|
|
7947
8002
|
* tests19:41/42). Whitespace stays an html child after
|
|
7948
8003
|
* the frameset (13.2.6.4.19, tests6:46). */
|
|
8004
|
+
const char* c = leptris_text_get_content(t);
|
|
7949
8005
|
int drop = 0;
|
|
7950
8006
|
if (b.whatwg && b.after_html && b.frameset) {
|
|
7951
|
-
for (const char* w =
|
|
8007
|
+
for (const char* w = c; *w; w++)
|
|
7952
8008
|
if (*w != ' ' && *w != '\t' &&
|
|
7953
8009
|
*w != '\n' && *w != '\r') {
|
|
7954
8010
|
drop = 1;
|
|
@@ -7956,12 +8012,8 @@ static LeptrisDocument html_parse_shared(
|
|
|
7956
8012
|
}
|
|
7957
8013
|
}
|
|
7958
8014
|
if (!drop) {
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
if (t) {
|
|
7962
|
-
if (!b.post_table_text) h_reconstruct(&b);
|
|
7963
|
-
h_append(&b, (LeptrisNodeRef)t);
|
|
7964
|
-
}
|
|
8015
|
+
if (!b.post_table_text) h_reconstruct(&b);
|
|
8016
|
+
h_append(&b, (LeptrisNodeRef)t);
|
|
7965
8017
|
}
|
|
7966
8018
|
}
|
|
7967
8019
|
}
|
|
@@ -1209,6 +1209,30 @@ static int op_value_of(XsltExec* ex, const XsltInstr* in,
|
|
|
1209
1209
|
* kind, markers stripped). */
|
|
1210
1210
|
char* sv = NULL;
|
|
1211
1211
|
if (in->select_is_dot) {
|
|
1212
|
+
/* #682 borrow fast path: a TEXT context node, or an ELEMENT
|
|
1213
|
+
* whose entire content is one text child, has its string-
|
|
1214
|
+
* value ALREADY materialized — append that pointer instead
|
|
1215
|
+
* of get_node_text's malloc + two-pass join + copy. op_text
|
|
1216
|
+
* copies synchronously, so the borrow never outlives this
|
|
1217
|
+
* call. */
|
|
1218
|
+
int ty = ((LeptrisNode*)node)->type;
|
|
1219
|
+
const char* borrowed = NULL;
|
|
1220
|
+
if (ty == LEPTRIS_NODE_TYPE_TEXT) {
|
|
1221
|
+
borrowed = leptris_text_get_content((LeptrisTextNode*)node);
|
|
1222
|
+
} else if (ty == LEPTRIS_NODE_TYPE_ELEMENT) {
|
|
1223
|
+
LeptrisNodeRef fc = leptris_elem_first_child(node);
|
|
1224
|
+
if (fc && ((LeptrisNode*)fc)->type == LEPTRIS_NODE_TYPE_TEXT &&
|
|
1225
|
+
!leptris_node_get_next_sibling(fc))
|
|
1226
|
+
borrowed =
|
|
1227
|
+
leptris_text_get_content((LeptrisTextNode*)fc);
|
|
1228
|
+
}
|
|
1229
|
+
if (borrowed) {
|
|
1230
|
+
op_text(ex, &(XsltInstr){ .kind = XSLT_INSTR_TEXT,
|
|
1231
|
+
.text = (char*)borrowed,
|
|
1232
|
+
.doe = in->doe },
|
|
1233
|
+
node);
|
|
1234
|
+
return 0;
|
|
1235
|
+
}
|
|
1212
1236
|
extern char* get_node_text(void* n);
|
|
1213
1237
|
sv = get_node_text(node);
|
|
1214
1238
|
} else if (in->select_attr_name) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
|
3
3
|
"name": "leptris",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.220",
|
|
5
5
|
"description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
|
|
6
6
|
"homepage": "https://github.com/leptris/leptris",
|
|
7
7
|
"license": "MIT",
|