webpack 5.108.4 → 5.109.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.
- package/lib/APIPlugin.js +48 -29
- package/lib/CacheFacade.js +8 -0
- package/lib/ChunkGroup.js +1 -0
- package/lib/CompatibilityPlugin.js +31 -29
- package/lib/Compilation.js +49 -4
- package/lib/ConstPlugin.js +11 -11
- package/lib/ContextModule.js +239 -6
- package/lib/ContextModuleFactory.js +139 -14
- package/lib/DefinePlugin.js +396 -101
- package/lib/Entrypoint.js +1 -1
- package/lib/ExportsInfoApiPlugin.js +2 -2
- package/lib/ExternalModule.js +107 -5
- package/lib/ExternalModuleFactoryPlugin.js +20 -1
- package/lib/FileSystemInfo.js +130 -74
- package/lib/HotModuleReplacementPlugin.js +18 -15
- package/lib/Module.js +3 -0
- package/lib/ModuleFilenameHelpers.js +2 -2
- package/lib/ModuleTypeConstants.js +8 -1
- package/lib/MultiCompiler.js +13 -2
- package/lib/MultiWatching.js +1 -0
- package/lib/NodeStuffPlugin.js +51 -33
- package/lib/NormalModule.js +169 -401
- package/lib/NormalModuleFactory.js +76 -2
- package/lib/ProgressPlugin.js +211 -119
- package/lib/ProvidePlugin.js +2 -2
- package/lib/RuntimeGlobals.js +47 -0
- package/lib/RuntimePlugin.js +33 -0
- package/lib/RuntimeTemplate.js +304 -16
- package/lib/UseStrictPlugin.js +1 -1
- package/lib/WebpackIsIncludedPlugin.js +1 -1
- package/lib/WebpackOptionsApply.js +24 -19
- package/lib/asset/AssetGenerator.js +110 -28
- package/lib/asset/AssetModulesPlugin.js +92 -2
- package/lib/asset/WebManifestGenerator.js +164 -0
- package/lib/asset/WebManifestParser.js +130 -0
- package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
- package/lib/async-modules/isGeneratorLowered.js +26 -0
- package/lib/cache/PackFileCacheStrategy.js +9 -3
- package/lib/config/browserslistTargetHandler.js +40 -0
- package/lib/config/defaults.js +219 -29
- package/lib/config/normalization.js +26 -0
- package/lib/config/target.js +9 -1
- package/lib/css/CssGenerator.js +40 -31
- package/lib/css/CssLoadingRuntimeModule.js +5 -1
- package/lib/css/CssModulesPlugin.js +13 -0
- package/lib/css/CssParser.js +594 -263
- package/lib/css/syntax.js +875 -462
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
- package/lib/dependencies/AMDPlugin.js +2 -2
- package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
- package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
- package/lib/dependencies/CommonJsExportsDependency.js +47 -2
- package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
- package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
- package/lib/dependencies/CommonJsPlugin.js +2 -2
- package/lib/dependencies/CommonJsRequireDependency.js +26 -1
- package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
- package/lib/dependencies/ContextDependencyHelpers.js +3 -3
- package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
- package/lib/dependencies/CssIcssExportDependency.js +162 -71
- package/lib/dependencies/CssUrlDependency.js +35 -4
- package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
- package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
- package/lib/dependencies/HarmonyImportDependency.js +12 -1
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
- package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
- package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
- package/lib/dependencies/HtmlEntryDependency.js +797 -172
- package/lib/dependencies/HtmlSourceDependency.js +26 -0
- package/lib/dependencies/ImportDependency.js +43 -1
- package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
- package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
- package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
- package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
- package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
- package/lib/dependencies/ImportMetaPlugin.js +532 -180
- package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
- package/lib/dependencies/ImportParserPlugin.js +80 -17
- package/lib/dependencies/ImportPhase.js +10 -8
- package/lib/dependencies/LoaderPlugin.js +5 -1
- package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
- package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
- package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
- package/lib/dependencies/SystemPlugin.js +2 -4
- package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
- package/lib/dependencies/URLDependency.js +141 -21
- package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
- package/lib/dependencies/WorkerDependency.js +86 -18
- package/lib/dependencies/WorkletDependency.js +278 -0
- package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
- package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
- package/lib/errors/LoaderLoadingError.js +20 -0
- package/lib/errors/ModuleParseError.js +39 -22
- package/lib/errors/NodeStuffInWebError.js +1 -1
- package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
- package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
- package/lib/hmr/lazyCompilationBackend.js +32 -8
- package/lib/html/HtmlGenerator.js +887 -15
- package/lib/html/HtmlModule.js +2 -2
- package/lib/html/HtmlModulesPlugin.js +610 -166
- package/lib/html/HtmlParser.js +603 -247
- package/lib/html/favicon.svg +1 -0
- package/lib/html/syntax.js +1264 -661
- package/lib/ids/IdHelpers.js +1 -1
- package/lib/index.js +6 -0
- package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
- package/lib/javascript/BasicEvaluatedExpression.js +366 -48
- package/lib/javascript/JavascriptGenerator.js +197 -49
- package/lib/javascript/JavascriptModulesPlugin.js +178 -24
- package/lib/javascript/JavascriptParser.js +701 -355
- package/lib/javascript/JavascriptParserHelpers.js +3 -6
- package/lib/javascript/syntax.js +4380 -0
- package/lib/library/ModuleLibraryPlugin.js +38 -1
- package/lib/loaders/LoaderRunner.js +714 -0
- package/lib/loaders/loadLoader.js +100 -0
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +19 -0
- package/lib/node/ReadFileCompileWasmPlugin.js +14 -0
- package/lib/optimize/ConcatenatedModule.js +120 -16
- package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
- package/lib/optimize/InnerGraphPlugin.js +50 -30
- package/lib/optimize/ModuleConcatenationPlugin.js +69 -3
- package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
- package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
- package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
- package/lib/prefetch/ResourceHintPlugin.js +663 -0
- package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
- package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
- package/lib/prefetch/parseResourceHintOptions.js +103 -0
- package/lib/rules/RuleSetCompiler.js +114 -0
- package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
- package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
- package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
- package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
- package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
- package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
- package/lib/schemes/HttpUriPlugin.js +40 -1
- package/lib/schemes/VirtualUrlPlugin.js +1 -2
- package/lib/serialization/FileMiddleware.js +29 -3
- package/lib/stats/DefaultStatsFactoryPlugin.js +32 -3
- package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
- package/lib/stats/DefaultStatsPrinterPlugin.js +12 -2
- package/lib/stats/StatsFactory.js +15 -8
- package/lib/url/URLParserPlugin.js +66 -13
- package/lib/util/AsyncQueue.js +122 -72
- package/lib/util/SetHelpers.js +1 -1
- package/lib/util/StackedMap.js +122 -48
- package/lib/util/binarySearchBounds.js +2 -2
- package/lib/util/concatenate.js +40 -14
- package/lib/util/createHooksRegistry.js +13 -5
- package/lib/util/dataURL.js +2 -2
- package/lib/util/deterministicGrouping.js +7 -5
- package/lib/util/formatSize.js +6 -2
- package/lib/util/globUtils.js +710 -0
- package/lib/util/identifier.js +23 -6
- package/lib/util/implicitTypeLoaderFallback.js +62 -0
- package/lib/util/internalSerializables.js +88 -71
- package/lib/util/magicComment.js +121 -25
- package/lib/util/numberHash.js +2 -2
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
- package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
- package/lib/web/FetchCompileWasmPlugin.js +14 -0
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
- package/lib/webpack.js +65 -0
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
- package/module.d.ts +21 -0
- package/package.json +13 -12
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +759 -36
- package/schemas/plugins/HtmlParserOptions.check.js +1 -1
- package/schemas/plugins/ProgressPlugin.check.js +1 -1
- package/schemas/plugins/ProgressPlugin.json +17 -1
- package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
- package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
- package/schemas/plugins/container/ExternalsType.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssParserOptions.check.js +1 -1
- package/types.d.ts +1213 -96
package/lib/html/syntax.js
CHANGED
|
@@ -147,6 +147,7 @@ const CC_QUESTION_MARK = 0x3f;
|
|
|
147
147
|
const CC_LEFT_SQUARE_BRACKET = 0x5b;
|
|
148
148
|
const CC_RIGHT_SQUARE_BRACKET = 0x5d;
|
|
149
149
|
const CC_GRAVE_ACCENT = 0x60;
|
|
150
|
+
const CC_NO_BREAK_SPACE = 0xa0;
|
|
150
151
|
|
|
151
152
|
const QUOTE_DOUBLE = 1;
|
|
152
153
|
const QUOTE_SINGLE = 2;
|
|
@@ -317,7 +318,7 @@ const getContentModeForTag = (name) => {
|
|
|
317
318
|
* @param {string} lit lowercase ASCII literal
|
|
318
319
|
* @returns {boolean} true if the range equals `lit` ignoring ASCII case
|
|
319
320
|
*/
|
|
320
|
-
const
|
|
321
|
+
const rangeEqualsLowerCase = (input, start, end, lit) => {
|
|
321
322
|
if (end - start !== lit.length) return false;
|
|
322
323
|
for (let i = 0; i < lit.length; i++) {
|
|
323
324
|
let c = input.charCodeAt(start + i);
|
|
@@ -339,27 +340,37 @@ const rangeEqualsLower = (input, start, end, lit) => {
|
|
|
339
340
|
const getContentModeForRange = (input, start, end) => {
|
|
340
341
|
switch (end - start) {
|
|
341
342
|
case 3:
|
|
342
|
-
if (
|
|
343
|
+
if (rangeEqualsLowerCase(input, start, end, "xmp")) return STATE_RAWTEXT;
|
|
343
344
|
return STATE_DATA;
|
|
344
345
|
case 5:
|
|
345
|
-
if (
|
|
346
|
-
if (
|
|
346
|
+
if (rangeEqualsLowerCase(input, start, end, "title")) return STATE_RCDATA;
|
|
347
|
+
if (rangeEqualsLowerCase(input, start, end, "style")) {
|
|
348
|
+
return STATE_RAWTEXT;
|
|
349
|
+
}
|
|
347
350
|
return STATE_DATA;
|
|
348
351
|
case 6:
|
|
349
|
-
if (
|
|
352
|
+
if (rangeEqualsLowerCase(input, start, end, "script")) {
|
|
350
353
|
return STATE_SCRIPT_DATA;
|
|
351
354
|
}
|
|
352
|
-
if (
|
|
355
|
+
if (rangeEqualsLowerCase(input, start, end, "iframe")) {
|
|
356
|
+
return STATE_RAWTEXT;
|
|
357
|
+
}
|
|
353
358
|
return STATE_DATA;
|
|
354
359
|
case 7:
|
|
355
|
-
if (
|
|
360
|
+
if (rangeEqualsLowerCase(input, start, end, "noembed")) {
|
|
361
|
+
return STATE_RAWTEXT;
|
|
362
|
+
}
|
|
356
363
|
return STATE_DATA;
|
|
357
364
|
case 8:
|
|
358
|
-
if (
|
|
359
|
-
|
|
365
|
+
if (rangeEqualsLowerCase(input, start, end, "textarea")) {
|
|
366
|
+
return STATE_RCDATA;
|
|
367
|
+
}
|
|
368
|
+
if (rangeEqualsLowerCase(input, start, end, "noframes")) {
|
|
369
|
+
return STATE_RAWTEXT;
|
|
370
|
+
}
|
|
360
371
|
return STATE_DATA;
|
|
361
372
|
case 9:
|
|
362
|
-
if (
|
|
373
|
+
if (rangeEqualsLowerCase(input, start, end, "plaintext")) {
|
|
363
374
|
return STATE_PLAINTEXT;
|
|
364
375
|
}
|
|
365
376
|
return STATE_DATA;
|
|
@@ -374,7 +385,7 @@ const getContentModeForRange = (input, start, end) => {
|
|
|
374
385
|
* @param {HtmlTokenCallbacks} callbacks callbacks
|
|
375
386
|
* @returns {number} final position
|
|
376
387
|
*/
|
|
377
|
-
const
|
|
388
|
+
const tokenize = (input, pos = 0, callbacks = {}) => {
|
|
378
389
|
const len = input.length;
|
|
379
390
|
let state = STATE_DATA;
|
|
380
391
|
let returnState = STATE_DATA;
|
|
@@ -406,6 +417,18 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
406
417
|
// Tracks whether the current tag has parsed any attributes — used to
|
|
407
418
|
// fire the `end-tag-with-attributes` parse error when an end tag emits.
|
|
408
419
|
let tagHasAttributes = false;
|
|
420
|
+
// Memoized next occurrence of `<` / `&` / NUL for the text-run
|
|
421
|
+
// fast-forwards: native `indexOf` beats a per-char JS loop, and `pos` only
|
|
422
|
+
// moves forward, so each memo is refreshed at most once per occurrence
|
|
423
|
+
// (`len` = no further occurrence).
|
|
424
|
+
let nextLt = -1;
|
|
425
|
+
let nextAmp = -1;
|
|
426
|
+
let nextNul = -1;
|
|
427
|
+
// Same memo scheme for the closing quote of quoted attribute values (long
|
|
428
|
+
// values: data: URIs, srcset, inline style). Memoized so a value with many
|
|
429
|
+
// `&` references doesn't re-run the quote scan per reference.
|
|
430
|
+
let nextDQuote = -1;
|
|
431
|
+
let nextSQuote = -1;
|
|
409
432
|
|
|
410
433
|
/**
|
|
411
434
|
* Reports a tokenizer parse error to the consumer. The offset range and
|
|
@@ -429,7 +452,7 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
429
452
|
* the accumulated `charRefCode`, if any. Used both inline (when the
|
|
430
453
|
* reference is terminated by a real next character) and at EOF (when the
|
|
431
454
|
* reference runs to the end of input). The scanner only flags the error —
|
|
432
|
-
* the spec's U+FFFD / Windows-1252 substitution is done by `
|
|
455
|
+
* the spec's U+FFFD / Windows-1252 substitution is done by `decodeEntities`.
|
|
433
456
|
* @param {number} endPos offset just past the reference
|
|
434
457
|
*/
|
|
435
458
|
const validateNumericReference = (endPos) => {
|
|
@@ -600,7 +623,7 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
600
623
|
// All WHATWG tokenizer states handled. Deliberately omitted parse errors
|
|
601
624
|
// (need state this offset scanner lacks): duplicate-attribute,
|
|
602
625
|
// cdata-in-html-content, `*-in-input-stream`. Reference substitution is
|
|
603
|
-
// left to `
|
|
626
|
+
// left to `decodeEntities`.
|
|
604
627
|
switch (state) {
|
|
605
628
|
// https://html.spec.whatwg.org/multipage/parsing.html#data-state
|
|
606
629
|
case STATE_DATA:
|
|
@@ -625,15 +648,23 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
625
648
|
pos++;
|
|
626
649
|
} else {
|
|
627
650
|
// Fast-forward over the run of ordinary text without re-entering
|
|
628
|
-
// the per-state switch
|
|
651
|
+
// the per-state switch — batches only the data state's "Anything
|
|
652
|
+
// else — emit the current input character" arc; the stops are
|
|
653
|
+
// exactly the code points with their own arcs (`<`, `&`, NUL).
|
|
629
654
|
pos++;
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
if (
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
pos
|
|
655
|
+
if (nextLt < pos) {
|
|
656
|
+
nextLt = input.indexOf("<", pos);
|
|
657
|
+
if (nextLt === -1) nextLt = len;
|
|
658
|
+
}
|
|
659
|
+
if (nextAmp < pos) {
|
|
660
|
+
nextAmp = input.indexOf("&", pos);
|
|
661
|
+
if (nextAmp === -1) nextAmp = len;
|
|
662
|
+
}
|
|
663
|
+
if (nextNul < pos) {
|
|
664
|
+
nextNul = input.indexOf("\0", pos);
|
|
665
|
+
if (nextNul === -1) nextNul = len;
|
|
636
666
|
}
|
|
667
|
+
pos = Math.min(nextLt, nextAmp, nextNul);
|
|
637
668
|
}
|
|
638
669
|
break;
|
|
639
670
|
|
|
@@ -993,19 +1024,22 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
993
1024
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
994
1025
|
pos++;
|
|
995
1026
|
} else {
|
|
996
|
-
// Fast-forward over the ordinary run of the quoted value
|
|
1027
|
+
// Fast-forward over the ordinary run of the quoted value with the
|
|
1028
|
+
// same memoized native-scan scheme as the data state.
|
|
997
1029
|
pos++;
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
if (
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
)
|
|
1005
|
-
break;
|
|
1006
|
-
}
|
|
1007
|
-
pos++;
|
|
1030
|
+
if (nextDQuote < pos) {
|
|
1031
|
+
nextDQuote = input.indexOf('"', pos);
|
|
1032
|
+
if (nextDQuote === -1) nextDQuote = len;
|
|
1033
|
+
}
|
|
1034
|
+
if (nextAmp < pos) {
|
|
1035
|
+
nextAmp = input.indexOf("&", pos);
|
|
1036
|
+
if (nextAmp === -1) nextAmp = len;
|
|
1008
1037
|
}
|
|
1038
|
+
if (nextNul < pos) {
|
|
1039
|
+
nextNul = input.indexOf("\0", pos);
|
|
1040
|
+
if (nextNul === -1) nextNul = len;
|
|
1041
|
+
}
|
|
1042
|
+
pos = Math.min(nextDQuote, nextAmp, nextNul);
|
|
1009
1043
|
}
|
|
1010
1044
|
break;
|
|
1011
1045
|
|
|
@@ -1029,15 +1063,22 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
1029
1063
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
1030
1064
|
pos++;
|
|
1031
1065
|
} else {
|
|
1032
|
-
// Fast-forward over the ordinary run of the quoted value
|
|
1066
|
+
// Fast-forward over the ordinary run of the quoted value with the
|
|
1067
|
+
// same memoized native-scan scheme as the data state.
|
|
1033
1068
|
pos++;
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
if (
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
pos
|
|
1069
|
+
if (nextSQuote < pos) {
|
|
1070
|
+
nextSQuote = input.indexOf("'", pos);
|
|
1071
|
+
if (nextSQuote === -1) nextSQuote = len;
|
|
1072
|
+
}
|
|
1073
|
+
if (nextAmp < pos) {
|
|
1074
|
+
nextAmp = input.indexOf("&", pos);
|
|
1075
|
+
if (nextAmp === -1) nextAmp = len;
|
|
1040
1076
|
}
|
|
1077
|
+
if (nextNul < pos) {
|
|
1078
|
+
nextNul = input.indexOf("\0", pos);
|
|
1079
|
+
if (nextNul === -1) nextNul = len;
|
|
1080
|
+
}
|
|
1081
|
+
pos = Math.min(nextSQuote, nextAmp, nextNul);
|
|
1041
1082
|
}
|
|
1042
1083
|
break;
|
|
1043
1084
|
|
|
@@ -1416,7 +1457,14 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
1416
1457
|
if (cc === CC_NULL) {
|
|
1417
1458
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
1418
1459
|
}
|
|
1460
|
+
// Fast-forward ordinary bogus-comment text without re-entering the
|
|
1461
|
+
// state switch; stop on the significant code points above.
|
|
1419
1462
|
pos++;
|
|
1463
|
+
while (pos < len) {
|
|
1464
|
+
const c2 = input.charCodeAt(pos);
|
|
1465
|
+
if (c2 === CC_GREATER_THAN || c2 === CC_NULL) break;
|
|
1466
|
+
pos++;
|
|
1467
|
+
}
|
|
1420
1468
|
}
|
|
1421
1469
|
break;
|
|
1422
1470
|
|
|
@@ -2263,7 +2311,10 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2263
2311
|
} else {
|
|
2264
2312
|
// Anything else
|
|
2265
2313
|
// Emit the current input character as a character token.
|
|
2266
|
-
|
|
2314
|
+
// Fast-forward to the next `]` (the only code point with its own
|
|
2315
|
+
// arc) in one native scan.
|
|
2316
|
+
pos = input.indexOf("]", pos + 1);
|
|
2317
|
+
if (pos === -1) pos = len;
|
|
2267
2318
|
}
|
|
2268
2319
|
break;
|
|
2269
2320
|
|
|
@@ -2332,15 +2383,23 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2332
2383
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
2333
2384
|
pos++;
|
|
2334
2385
|
} else {
|
|
2335
|
-
// Fast-forward over ordinary RCDATA text
|
|
2386
|
+
// Fast-forward over ordinary RCDATA text (same memoized
|
|
2387
|
+
// `indexOf` scheme as the data state) — batches only this
|
|
2388
|
+
// state's "anything else" arc; `&` / `<` / NUL keep their own.
|
|
2336
2389
|
pos++;
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
if (
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
pos
|
|
2390
|
+
if (nextLt < pos) {
|
|
2391
|
+
nextLt = input.indexOf("<", pos);
|
|
2392
|
+
if (nextLt === -1) nextLt = len;
|
|
2393
|
+
}
|
|
2394
|
+
if (nextAmp < pos) {
|
|
2395
|
+
nextAmp = input.indexOf("&", pos);
|
|
2396
|
+
if (nextAmp === -1) nextAmp = len;
|
|
2397
|
+
}
|
|
2398
|
+
if (nextNul < pos) {
|
|
2399
|
+
nextNul = input.indexOf("\0", pos);
|
|
2400
|
+
if (nextNul === -1) nextNul = len;
|
|
2343
2401
|
}
|
|
2402
|
+
pos = Math.min(nextLt, nextAmp, nextNul);
|
|
2344
2403
|
}
|
|
2345
2404
|
break;
|
|
2346
2405
|
|
|
@@ -2395,7 +2454,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2395
2454
|
if (isSpace(cc)) {
|
|
2396
2455
|
tagNameEnd = pos;
|
|
2397
2456
|
if (
|
|
2398
|
-
|
|
2457
|
+
rangeEqualsLowerCase(
|
|
2458
|
+
input,
|
|
2459
|
+
tagNameStart,
|
|
2460
|
+
tagNameEnd,
|
|
2461
|
+
lastOpenTagName
|
|
2462
|
+
)
|
|
2399
2463
|
) {
|
|
2400
2464
|
flushText(tagStart);
|
|
2401
2465
|
state = STATE_BEFORE_ATTRIBUTE_NAME;
|
|
@@ -2411,7 +2475,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2411
2475
|
// "anything else" entry below.
|
|
2412
2476
|
tagNameEnd = pos;
|
|
2413
2477
|
if (
|
|
2414
|
-
|
|
2478
|
+
rangeEqualsLowerCase(
|
|
2479
|
+
input,
|
|
2480
|
+
tagNameStart,
|
|
2481
|
+
tagNameEnd,
|
|
2482
|
+
lastOpenTagName
|
|
2483
|
+
)
|
|
2415
2484
|
) {
|
|
2416
2485
|
flushText(tagStart);
|
|
2417
2486
|
state = STATE_SELF_CLOSING_START_TAG;
|
|
@@ -2427,7 +2496,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2427
2496
|
// per the "anything else" entry below.
|
|
2428
2497
|
tagNameEnd = pos;
|
|
2429
2498
|
if (
|
|
2430
|
-
|
|
2499
|
+
rangeEqualsLowerCase(
|
|
2500
|
+
input,
|
|
2501
|
+
tagNameStart,
|
|
2502
|
+
tagNameEnd,
|
|
2503
|
+
lastOpenTagName
|
|
2504
|
+
)
|
|
2431
2505
|
) {
|
|
2432
2506
|
flushText(tagStart);
|
|
2433
2507
|
state = STATE_DATA;
|
|
@@ -2467,13 +2541,19 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2467
2541
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
2468
2542
|
pos++;
|
|
2469
2543
|
} else {
|
|
2470
|
-
// Fast-forward over ordinary RAWTEXT text
|
|
2544
|
+
// Fast-forward over ordinary RAWTEXT text (same memoized
|
|
2545
|
+
// `indexOf` scheme as the data state) — batches only this
|
|
2546
|
+
// state's "anything else" arc; `<` / NUL keep their own.
|
|
2471
2547
|
pos++;
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
if (
|
|
2475
|
-
|
|
2548
|
+
if (nextLt < pos) {
|
|
2549
|
+
nextLt = input.indexOf("<", pos);
|
|
2550
|
+
if (nextLt === -1) nextLt = len;
|
|
2551
|
+
}
|
|
2552
|
+
if (nextNul < pos) {
|
|
2553
|
+
nextNul = input.indexOf("\0", pos);
|
|
2554
|
+
if (nextNul === -1) nextNul = len;
|
|
2476
2555
|
}
|
|
2556
|
+
pos = Math.min(nextLt, nextNul);
|
|
2477
2557
|
}
|
|
2478
2558
|
break;
|
|
2479
2559
|
|
|
@@ -2527,7 +2607,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2527
2607
|
if (isSpace(cc)) {
|
|
2528
2608
|
tagNameEnd = pos;
|
|
2529
2609
|
if (
|
|
2530
|
-
|
|
2610
|
+
rangeEqualsLowerCase(
|
|
2611
|
+
input,
|
|
2612
|
+
tagNameStart,
|
|
2613
|
+
tagNameEnd,
|
|
2614
|
+
lastOpenTagName
|
|
2615
|
+
)
|
|
2531
2616
|
) {
|
|
2532
2617
|
flushText(tagStart);
|
|
2533
2618
|
state = STATE_BEFORE_ATTRIBUTE_NAME;
|
|
@@ -2542,7 +2627,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2542
2627
|
// "anything else" entry below.
|
|
2543
2628
|
tagNameEnd = pos;
|
|
2544
2629
|
if (
|
|
2545
|
-
|
|
2630
|
+
rangeEqualsLowerCase(
|
|
2631
|
+
input,
|
|
2632
|
+
tagNameStart,
|
|
2633
|
+
tagNameEnd,
|
|
2634
|
+
lastOpenTagName
|
|
2635
|
+
)
|
|
2546
2636
|
) {
|
|
2547
2637
|
flushText(tagStart);
|
|
2548
2638
|
state = STATE_SELF_CLOSING_START_TAG;
|
|
@@ -2557,7 +2647,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2557
2647
|
// per the "anything else" entry below.
|
|
2558
2648
|
tagNameEnd = pos;
|
|
2559
2649
|
if (
|
|
2560
|
-
|
|
2650
|
+
rangeEqualsLowerCase(
|
|
2651
|
+
input,
|
|
2652
|
+
tagNameStart,
|
|
2653
|
+
tagNameEnd,
|
|
2654
|
+
lastOpenTagName
|
|
2655
|
+
)
|
|
2561
2656
|
) {
|
|
2562
2657
|
flushText(tagStart);
|
|
2563
2658
|
state = STATE_DATA;
|
|
@@ -2596,13 +2691,19 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2596
2691
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
2597
2692
|
pos++;
|
|
2598
2693
|
} else {
|
|
2599
|
-
// Fast-forward over ordinary script-data text
|
|
2694
|
+
// Fast-forward over ordinary script-data text (same memoized
|
|
2695
|
+
// `indexOf` scheme as the data state) — batches only this
|
|
2696
|
+
// state's "anything else" arc; `<` / NUL keep their own.
|
|
2600
2697
|
pos++;
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
if (
|
|
2604
|
-
pos++;
|
|
2698
|
+
if (nextLt < pos) {
|
|
2699
|
+
nextLt = input.indexOf("<", pos);
|
|
2700
|
+
if (nextLt === -1) nextLt = len;
|
|
2605
2701
|
}
|
|
2702
|
+
if (nextNul < pos) {
|
|
2703
|
+
nextNul = input.indexOf("\0", pos);
|
|
2704
|
+
if (nextNul === -1) nextNul = len;
|
|
2705
|
+
}
|
|
2706
|
+
pos = Math.min(nextLt, nextNul);
|
|
2606
2707
|
}
|
|
2607
2708
|
break;
|
|
2608
2709
|
|
|
@@ -2662,7 +2763,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2662
2763
|
if (isSpace(cc)) {
|
|
2663
2764
|
tagNameEnd = pos;
|
|
2664
2765
|
if (
|
|
2665
|
-
|
|
2766
|
+
rangeEqualsLowerCase(
|
|
2767
|
+
input,
|
|
2768
|
+
tagNameStart,
|
|
2769
|
+
tagNameEnd,
|
|
2770
|
+
lastOpenTagName
|
|
2771
|
+
)
|
|
2666
2772
|
) {
|
|
2667
2773
|
flushText(tagStart);
|
|
2668
2774
|
state = STATE_BEFORE_ATTRIBUTE_NAME;
|
|
@@ -2677,7 +2783,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2677
2783
|
// "anything else" entry below.
|
|
2678
2784
|
tagNameEnd = pos;
|
|
2679
2785
|
if (
|
|
2680
|
-
|
|
2786
|
+
rangeEqualsLowerCase(
|
|
2787
|
+
input,
|
|
2788
|
+
tagNameStart,
|
|
2789
|
+
tagNameEnd,
|
|
2790
|
+
lastOpenTagName
|
|
2791
|
+
)
|
|
2681
2792
|
) {
|
|
2682
2793
|
flushText(tagStart);
|
|
2683
2794
|
state = STATE_SELF_CLOSING_START_TAG;
|
|
@@ -2692,7 +2803,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2692
2803
|
// per the "anything else" entry below.
|
|
2693
2804
|
tagNameEnd = pos;
|
|
2694
2805
|
if (
|
|
2695
|
-
|
|
2806
|
+
rangeEqualsLowerCase(
|
|
2807
|
+
input,
|
|
2808
|
+
tagNameStart,
|
|
2809
|
+
tagNameEnd,
|
|
2810
|
+
lastOpenTagName
|
|
2811
|
+
)
|
|
2696
2812
|
) {
|
|
2697
2813
|
flushText(tagStart);
|
|
2698
2814
|
state = STATE_DATA;
|
|
@@ -2770,7 +2886,20 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2770
2886
|
if (cc === CC_NULL) {
|
|
2771
2887
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
2772
2888
|
}
|
|
2889
|
+
// Fast-forward ordinary escaped script text without re-entering
|
|
2890
|
+
// the state switch; stop on the significant code points above.
|
|
2773
2891
|
pos++;
|
|
2892
|
+
while (pos < len) {
|
|
2893
|
+
const c2 = input.charCodeAt(pos);
|
|
2894
|
+
if (
|
|
2895
|
+
c2 === CC_HYPHEN_MINUS ||
|
|
2896
|
+
c2 === CC_LESS_THAN ||
|
|
2897
|
+
c2 === CC_NULL
|
|
2898
|
+
) {
|
|
2899
|
+
break;
|
|
2900
|
+
}
|
|
2901
|
+
pos++;
|
|
2902
|
+
}
|
|
2774
2903
|
}
|
|
2775
2904
|
break;
|
|
2776
2905
|
|
|
@@ -2890,7 +3019,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2890
3019
|
if (isSpace(cc)) {
|
|
2891
3020
|
tagNameEnd = pos;
|
|
2892
3021
|
if (
|
|
2893
|
-
|
|
3022
|
+
rangeEqualsLowerCase(
|
|
3023
|
+
input,
|
|
3024
|
+
tagNameStart,
|
|
3025
|
+
tagNameEnd,
|
|
3026
|
+
lastOpenTagName
|
|
3027
|
+
)
|
|
2894
3028
|
) {
|
|
2895
3029
|
flushText(tagStart);
|
|
2896
3030
|
state = STATE_BEFORE_ATTRIBUTE_NAME;
|
|
@@ -2905,7 +3039,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2905
3039
|
// "anything else" entry below.
|
|
2906
3040
|
tagNameEnd = pos;
|
|
2907
3041
|
if (
|
|
2908
|
-
|
|
3042
|
+
rangeEqualsLowerCase(
|
|
3043
|
+
input,
|
|
3044
|
+
tagNameStart,
|
|
3045
|
+
tagNameEnd,
|
|
3046
|
+
lastOpenTagName
|
|
3047
|
+
)
|
|
2909
3048
|
) {
|
|
2910
3049
|
flushText(tagStart);
|
|
2911
3050
|
state = STATE_SELF_CLOSING_START_TAG;
|
|
@@ -2920,7 +3059,12 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
2920
3059
|
// per the "anything else" entry below.
|
|
2921
3060
|
tagNameEnd = pos;
|
|
2922
3061
|
if (
|
|
2923
|
-
|
|
3062
|
+
rangeEqualsLowerCase(
|
|
3063
|
+
input,
|
|
3064
|
+
tagNameStart,
|
|
3065
|
+
tagNameEnd,
|
|
3066
|
+
lastOpenTagName
|
|
3067
|
+
)
|
|
2924
3068
|
) {
|
|
2925
3069
|
flushText(tagStart);
|
|
2926
3070
|
state = STATE_DATA;
|
|
@@ -3002,7 +3146,20 @@ const walkHtmlTokens = (input, pos = 0, callbacks = {}) => {
|
|
|
3002
3146
|
if (cc === CC_NULL) {
|
|
3003
3147
|
reportError("unexpected-null-character", pos, pos + 1, "warning");
|
|
3004
3148
|
}
|
|
3149
|
+
// Fast-forward ordinary double-escaped script text without re-entering
|
|
3150
|
+
// the state switch; stop on the significant code points above.
|
|
3005
3151
|
pos++;
|
|
3152
|
+
while (pos < len) {
|
|
3153
|
+
const c2 = input.charCodeAt(pos);
|
|
3154
|
+
if (
|
|
3155
|
+
c2 === CC_HYPHEN_MINUS ||
|
|
3156
|
+
c2 === CC_LESS_THAN ||
|
|
3157
|
+
c2 === CC_NULL
|
|
3158
|
+
) {
|
|
3159
|
+
break;
|
|
3160
|
+
}
|
|
3161
|
+
pos++;
|
|
3162
|
+
}
|
|
3006
3163
|
}
|
|
3007
3164
|
break;
|
|
3008
3165
|
|
|
@@ -3618,15 +3775,6 @@ const decodeNumericReference = (code) => {
|
|
|
3618
3775
|
return String.fromCodePoint(code);
|
|
3619
3776
|
};
|
|
3620
3777
|
|
|
3621
|
-
// Match one of three forms (each with an optional trailing `;`):
|
|
3622
|
-
// `&#x<hex>` - hex numeric reference (requires the `x`/`X`).
|
|
3623
|
-
// `&#<dec>` - decimal numeric reference (digits only).
|
|
3624
|
-
// `&<name>` - named reference (letter followed by alphanumerics).
|
|
3625
|
-
// The three alternatives are kept separate so a decimal reference like
|
|
3626
|
-
// `Ab` doesn't greedily eat the trailing `b` as if it were hex.
|
|
3627
|
-
const CHARACTER_REFERENCE_REGEXP =
|
|
3628
|
-
/&(?:#[xX][0-9a-fA-F]+|#[0-9]+|[a-zA-Z][a-zA-Z0-9]*);?/g;
|
|
3629
|
-
|
|
3630
3778
|
/**
|
|
3631
3779
|
* Decode a single matched character reference.
|
|
3632
3780
|
* @param {string} match the matched reference text
|
|
@@ -3634,45 +3782,34 @@ const CHARACTER_REFERENCE_REGEXP =
|
|
|
3634
3782
|
* @param {boolean=} isAttribute true when the match came from an attribute value
|
|
3635
3783
|
* @returns {string} decoded text, or `match` itself when it stays literal
|
|
3636
3784
|
*/
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
return decodeNumericReference(Number.parseInt(body, isHex ? 16 : 10));
|
|
3652
|
-
}
|
|
3653
|
-
|
|
3654
|
-
// Named reference. Try the full captured name first, then
|
|
3655
|
-
// progressively shorter prefixes - this handles direct matches
|
|
3656
|
-
// like `&` as well as WHATWG longest-prefix semantics where
|
|
3657
|
-
// e.g. `¬pre;` decodes as `¬` (a legacy bare entity)
|
|
3658
|
-
// followed by `pre;` as literal text.
|
|
3659
|
-
const name = match.slice(1);
|
|
3785
|
+
/**
|
|
3786
|
+
* Decode a named character reference spanning `[start, end)` of `str` (the
|
|
3787
|
+
* `&` at `start`, the optional `;` included). Slices the name once; WHATWG
|
|
3788
|
+
* longest-prefix and consumed-as-part-of-an-attribute semantics as before.
|
|
3789
|
+
* @param {string} str the raw string
|
|
3790
|
+
* @param {number} start offset of the reference's `&`
|
|
3791
|
+
* @param {number} end offset just past the reference (past the `;` if present)
|
|
3792
|
+
* @param {number} nextCharCode char code following the reference (NaN at the end)
|
|
3793
|
+
* @param {boolean=} isAttribute true when the reference came from an attribute value
|
|
3794
|
+
* @returns {string | undefined} decoded text, or `undefined` when it stays literal
|
|
3795
|
+
*/
|
|
3796
|
+
const decodeNamedReference = (str, start, end, nextCharCode, isAttribute) => {
|
|
3797
|
+
// Mirrors the old `match.slice(1)`: the name keeps its trailing `;`.
|
|
3798
|
+
const name = str.slice(start + 1, end);
|
|
3660
3799
|
const matchEndsWithSemi = name.charCodeAt(name.length - 1) === 0x3b;
|
|
3661
3800
|
|
|
3662
3801
|
// Attribute-context guard: if the entity match didn't end with `;`
|
|
3663
3802
|
// and the next character in the source is `=` or ASCII
|
|
3664
3803
|
// alphanumeric, the WHATWG spec says to flush the literal text
|
|
3665
|
-
// rather than decode. The greedy
|
|
3804
|
+
// rather than decode. The greedy scan already absorbed any
|
|
3666
3805
|
// trailing alphanumerics, so the only candidate "next char" here
|
|
3667
3806
|
// is `=` (or any non-alphanumeric).
|
|
3668
3807
|
if (isAttribute && !matchEndsWithSemi && nextCharCode === 0x3d /* = */) {
|
|
3669
|
-
return
|
|
3808
|
+
return undefined;
|
|
3670
3809
|
}
|
|
3671
3810
|
|
|
3672
|
-
// Fast path: the
|
|
3673
|
-
// `<`, ` `,
|
|
3674
|
-
// full-length `name.slice(0, name.length)` the loop's first iteration
|
|
3675
|
-
// would allocate. No leftover, so the attribute guard never applies.
|
|
3811
|
+
// Fast path: the scan usually captures exactly one entity (`&`,
|
|
3812
|
+
// `<`, ` `, ...), so the whole `name` is the match.
|
|
3676
3813
|
if (name.length <= MAX_ENTITY_NAME_LEN) {
|
|
3677
3814
|
const full = HTML_ENTITIES[name];
|
|
3678
3815
|
if (full !== undefined) return full;
|
|
@@ -3691,31 +3828,20 @@ const decodeOneReference = (match, nextCharCode, isAttribute) => {
|
|
|
3691
3828
|
// Attribute-context longest-prefix guard: if the matched
|
|
3692
3829
|
// prefix doesn't end with `;` and the leftover starts with
|
|
3693
3830
|
// an alphanumeric character, leave literal per WHATWG.
|
|
3694
|
-
// (The regex greedy-consumes alphanumerics, so any leftover
|
|
3695
|
-
// within `name` is itself alphanumeric — we only need to
|
|
3696
|
-
// check non-empty leftover here; the `=` case is handled
|
|
3697
|
-
// above against the source character after the match.)
|
|
3698
3831
|
if (
|
|
3699
3832
|
isAttribute &&
|
|
3700
3833
|
i < name.length &&
|
|
3701
3834
|
prefix.charCodeAt(prefix.length - 1) !== 0x3b
|
|
3702
3835
|
) {
|
|
3703
|
-
return
|
|
3836
|
+
return undefined;
|
|
3704
3837
|
}
|
|
3705
3838
|
return HTML_ENTITIES[prefix] + name.slice(i);
|
|
3706
3839
|
}
|
|
3707
3840
|
}
|
|
3708
|
-
return
|
|
3841
|
+
return undefined;
|
|
3709
3842
|
};
|
|
3710
3843
|
|
|
3711
|
-
|
|
3712
|
-
// per decode call, and the callback stays monomorphic.
|
|
3713
|
-
/** @type {(match: string, offset: number, source: string) => string} */
|
|
3714
|
-
const _decodeReferenceInText = (match, offset, source) =>
|
|
3715
|
-
decodeOneReference(match, source.charCodeAt(offset + match.length), false);
|
|
3716
|
-
/** @type {(match: string, offset: number, source: string) => string} */
|
|
3717
|
-
const _decodeReferenceInAttribute = (match, offset, source) =>
|
|
3718
|
-
decodeOneReference(match, source.charCodeAt(offset + match.length), true);
|
|
3844
|
+
/** @typedef {{ text: string, map: number[] | undefined }} DecodedEntitiesWithMap */
|
|
3719
3845
|
|
|
3720
3846
|
/**
|
|
3721
3847
|
* Decode HTML character references in a string. Handles all numeric
|
|
@@ -3728,67 +3854,246 @@ const _decodeReferenceInAttribute = (match, offset, source) =>
|
|
|
3728
3854
|
* trailing `;` whose next character is `=` or ASCII alphanumeric is left
|
|
3729
3855
|
* undecoded, so e.g. `&=foo` stays literal in an attribute value but
|
|
3730
3856
|
* decodes to `&=foo` in text.
|
|
3857
|
+
*
|
|
3858
|
+
* With `withMap` the result also carries a boundary map from the decoded
|
|
3859
|
+
* string back to raw offsets, so spans computed on the decoded text (e.g.
|
|
3860
|
+
* srcset candidate URLs) can be translated to source ranges. `map[i]` is the
|
|
3861
|
+
* raw offset of decoded boundary `i` (`0..text.length`); boundaries inside a
|
|
3862
|
+
* reference's decoded text map to the reference start. `map` is `undefined`
|
|
3863
|
+
* when nothing was decoded (then `text === str`).
|
|
3864
|
+
* @overload
|
|
3731
3865
|
* @param {string} str the raw string from the token slice
|
|
3732
3866
|
* @param {boolean=} isAttribute true if `str` came from an attribute value
|
|
3733
3867
|
* @returns {string} decoded string
|
|
3734
3868
|
*/
|
|
3735
|
-
const decodeHtmlEntities = (str, isAttribute) => {
|
|
3736
|
-
if (!str.includes("&")) return str;
|
|
3737
|
-
|
|
3738
|
-
return str.replace(
|
|
3739
|
-
CHARACTER_REFERENCE_REGEXP,
|
|
3740
|
-
isAttribute ? _decodeReferenceInAttribute : _decodeReferenceInText
|
|
3741
|
-
);
|
|
3742
|
-
};
|
|
3743
|
-
|
|
3744
3869
|
/**
|
|
3745
|
-
*
|
|
3746
|
-
*
|
|
3747
|
-
*
|
|
3748
|
-
*
|
|
3749
|
-
*
|
|
3750
|
-
|
|
3870
|
+
* @overload
|
|
3871
|
+
* @param {string} str the raw string from the token slice
|
|
3872
|
+
* @param {boolean | undefined} isAttribute true if `str` came from an attribute value
|
|
3873
|
+
* @param {true} withMap also build the decoded-to-raw boundary map
|
|
3874
|
+
* @returns {DecodedEntitiesWithMap} decoded text and offset map
|
|
3875
|
+
*/
|
|
3876
|
+
/**
|
|
3751
3877
|
* @param {string} str the raw string from the token slice
|
|
3752
3878
|
* @param {boolean=} isAttribute true if `str` came from an attribute value
|
|
3753
|
-
* @
|
|
3879
|
+
* @param {boolean=} withMap also build the decoded-to-raw boundary map
|
|
3880
|
+
* @returns {string | DecodedEntitiesWithMap} decoded string (with map when `withMap`)
|
|
3754
3881
|
*/
|
|
3755
|
-
const
|
|
3882
|
+
const decodeEntities = (str, isAttribute, withMap) => {
|
|
3883
|
+
// Hand-rolled scan of one of three reference forms, each with an optional
|
|
3884
|
+
// trailing `;` — `&#x<hex>+` / `&#<dec>+` / `&<alpha><alnum>*` (kept separate
|
|
3885
|
+
// so `Ab` doesn't eat the `b` as hex): entity-dense text pays no regex
|
|
3886
|
+
// machinery or per-match object, and a string whose references all stay
|
|
3887
|
+
// literal is returned unchanged without rebuilding. Map bookkeeping is
|
|
3888
|
+
// gated on `withMap` at the rare per-decoded-reference points, so the
|
|
3889
|
+
// dominant no-map mode pays nothing for the unified implementation.
|
|
3890
|
+
let amp = str.indexOf("&");
|
|
3891
|
+
if (amp === -1) return withMap ? { text: str, map: undefined } : str;
|
|
3892
|
+
let out = "";
|
|
3893
|
+
let last = 0;
|
|
3756
3894
|
/** @type {number[] | undefined} */
|
|
3757
3895
|
let map;
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
let
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3896
|
+
do {
|
|
3897
|
+
// `end` stays 0 when the `&` doesn't start a well-formed reference; a
|
|
3898
|
+
// numeric reference's code point accumulates during the digit scan
|
|
3899
|
+
// (saturating above the code-point range — every overflow decodes the
|
|
3900
|
+
// same replacement) so it needs no slice at all.
|
|
3901
|
+
let end = 0;
|
|
3902
|
+
let code = -1;
|
|
3903
|
+
let cc = str.charCodeAt(amp + 1);
|
|
3904
|
+
if (cc === 0x23 /* # */) {
|
|
3905
|
+
cc = str.charCodeAt(amp + 2);
|
|
3906
|
+
let p;
|
|
3907
|
+
if (cc === 0x78 /* x */ || cc === 0x58 /* X */) {
|
|
3908
|
+
code = 0;
|
|
3909
|
+
p = amp + 3;
|
|
3910
|
+
while (isAsciiHexDigit((cc = str.charCodeAt(p)))) {
|
|
3911
|
+
code = code * 16 + (cc <= 0x39 ? cc - 0x30 : (cc | 0x20) - 0x57);
|
|
3912
|
+
if (code > 0x10ffff) code = 0x110000;
|
|
3913
|
+
p++;
|
|
3914
|
+
}
|
|
3915
|
+
if (p > amp + 3) end = p;
|
|
3916
|
+
} else {
|
|
3917
|
+
code = 0;
|
|
3918
|
+
p = amp + 2;
|
|
3919
|
+
while (isAsciiDigit((cc = str.charCodeAt(p)))) {
|
|
3920
|
+
code = code * 10 + (cc - 0x30);
|
|
3921
|
+
if (code > 0x10ffff) code = 0x110000;
|
|
3922
|
+
p++;
|
|
3923
|
+
}
|
|
3924
|
+
if (p > amp + 2) end = p;
|
|
3925
|
+
}
|
|
3926
|
+
} else if (isAsciiAlpha(cc)) {
|
|
3927
|
+
code = -1;
|
|
3928
|
+
let p = amp + 2;
|
|
3929
|
+
while (isAsciiAlphanumeric(str.charCodeAt(p))) p++;
|
|
3930
|
+
end = p;
|
|
3931
|
+
}
|
|
3932
|
+
if (end !== 0) {
|
|
3933
|
+
if (str.charCodeAt(end) === 0x3b /* ; */) end++;
|
|
3934
|
+
// Numeric references always decode; named ones may stay literal.
|
|
3935
|
+
const decoded =
|
|
3936
|
+
code >= 0
|
|
3937
|
+
? decodeNumericReference(code)
|
|
3938
|
+
: decodeNamedReference(
|
|
3939
|
+
str,
|
|
3940
|
+
amp,
|
|
3941
|
+
end,
|
|
3942
|
+
str.charCodeAt(end),
|
|
3943
|
+
isAttribute
|
|
3944
|
+
);
|
|
3945
|
+
if (decoded !== undefined) {
|
|
3946
|
+
if (withMap) map = _mapReference(map, last, amp, decoded.length);
|
|
3947
|
+
out += str.slice(last, amp);
|
|
3948
|
+
out += decoded;
|
|
3949
|
+
last = end;
|
|
3950
|
+
}
|
|
3951
|
+
amp = str.indexOf("&", end);
|
|
3952
|
+
} else {
|
|
3953
|
+
amp = str.indexOf("&", amp + 1);
|
|
3954
|
+
}
|
|
3955
|
+
} while (amp !== -1);
|
|
3956
|
+
if (last === 0) return withMap ? { text: str, map: undefined } : str;
|
|
3957
|
+
const text = out + str.slice(last);
|
|
3958
|
+
return withMap
|
|
3959
|
+
? _finishMap(text, /** @type {number[]} */ (map), last, str.length)
|
|
3960
|
+
: text;
|
|
3961
|
+
};
|
|
3962
|
+
|
|
3963
|
+
// Map-mode bookkeeping for `decodeEntities`, kept out of the scanner body so
|
|
3964
|
+
// the dominant no-map mode stays small enough to inline.
|
|
3965
|
+
/**
|
|
3966
|
+
* @param {number[] | undefined} map boundary map so far
|
|
3967
|
+
* @param {number} last raw offset the literal run starts at
|
|
3968
|
+
* @param {number} amp raw offset of the decoded reference
|
|
3969
|
+
* @param {number} decodedLength length of the decoded text
|
|
3970
|
+
* @returns {number[]} the map, with the literal run's and reference's boundaries appended
|
|
3971
|
+
*/
|
|
3972
|
+
const _mapReference = (map, last, amp, decodedLength) => {
|
|
3973
|
+
if (map === undefined) map = [];
|
|
3974
|
+
for (let r = last; r < amp; r++) map.push(r);
|
|
3975
|
+
for (let i = 0; i < decodedLength; i++) map.push(amp);
|
|
3976
|
+
return map;
|
|
3977
|
+
};
|
|
3978
|
+
|
|
3979
|
+
/**
|
|
3980
|
+
* @param {string} text decoded text
|
|
3981
|
+
* @param {number[]} map boundary map so far
|
|
3982
|
+
* @param {number} last raw offset of the trailing literal run
|
|
3983
|
+
* @param {number} rawLength raw string length
|
|
3984
|
+
* @returns {DecodedEntitiesWithMap} the finished result
|
|
3985
|
+
*/
|
|
3986
|
+
const _finishMap = (text, map, last, rawLength) => {
|
|
3987
|
+
for (let r = last; r < rawLength; r++) map.push(r);
|
|
3988
|
+
map.push(rawLength);
|
|
3989
|
+
return { text, map };
|
|
3990
|
+
};
|
|
3991
|
+
|
|
3992
|
+
/**
|
|
3993
|
+
* Escape a string per the WHATWG "escape a string" algorithm
|
|
3994
|
+
* (https://html.spec.whatwg.org/multipage/parsing.html#escapingString) in
|
|
3995
|
+
* attribute mode: `&`, U+00A0 and `"`. CR/LF are additionally encoded as
|
|
3996
|
+
* numeric references so the result stays single-line (usable in a `data:`
|
|
3997
|
+
* URI). Single pass, returns the input unchanged when nothing needs escaping.
|
|
3998
|
+
* @param {string} s string to escape
|
|
3999
|
+
* @returns {string} HTML attribute-safe string
|
|
4000
|
+
*/
|
|
4001
|
+
const escapeAttribute = (s) => {
|
|
4002
|
+
// fast path: native indexOf scans beat any per-character loop
|
|
4003
|
+
if (
|
|
4004
|
+
!s.includes("&") &&
|
|
4005
|
+
!s.includes('"') &&
|
|
4006
|
+
!s.includes("\u00A0") &&
|
|
4007
|
+
!s.includes("\n") &&
|
|
4008
|
+
!s.includes("\r")
|
|
4009
|
+
) {
|
|
4010
|
+
return s;
|
|
4011
|
+
}
|
|
4012
|
+
let out = "";
|
|
4013
|
+
let last = 0;
|
|
4014
|
+
for (let i = 0; i < s.length; i++) {
|
|
4015
|
+
let rep;
|
|
4016
|
+
switch (s.charCodeAt(i)) {
|
|
4017
|
+
case CC_AMPERSAND:
|
|
4018
|
+
rep = "&";
|
|
4019
|
+
break;
|
|
4020
|
+
case CC_QUOTATION_MARK:
|
|
4021
|
+
rep = """;
|
|
4022
|
+
break;
|
|
4023
|
+
case CC_NO_BREAK_SPACE:
|
|
4024
|
+
rep = " ";
|
|
4025
|
+
break;
|
|
4026
|
+
case CC_LF:
|
|
4027
|
+
rep = " ";
|
|
4028
|
+
break;
|
|
4029
|
+
case CC_CR:
|
|
4030
|
+
rep = " ";
|
|
4031
|
+
break;
|
|
4032
|
+
default:
|
|
4033
|
+
continue;
|
|
3783
4034
|
}
|
|
4035
|
+
out += s.slice(last, i) + rep;
|
|
4036
|
+
last = i + 1;
|
|
3784
4037
|
}
|
|
3785
|
-
return
|
|
4038
|
+
return out + s.slice(last);
|
|
4039
|
+
};
|
|
4040
|
+
|
|
4041
|
+
/**
|
|
4042
|
+
* Escape a string per the WHATWG "escape a string" algorithm in text mode:
|
|
4043
|
+
* `&`, U+00A0, `<` and `>`. CR/LF are additionally encoded as numeric
|
|
4044
|
+
* references so the result stays single-line (usable in a `data:` URI).
|
|
4045
|
+
* Single pass, returns the input unchanged when nothing needs escaping.
|
|
4046
|
+
* @param {string} s string to escape
|
|
4047
|
+
* @returns {string} HTML text-content-safe string
|
|
4048
|
+
*/
|
|
4049
|
+
const escapeText = (s) => {
|
|
4050
|
+
// fast path: native indexOf scans beat any per-character loop
|
|
4051
|
+
if (
|
|
4052
|
+
!s.includes("&") &&
|
|
4053
|
+
!s.includes("<") &&
|
|
4054
|
+
!s.includes(">") &&
|
|
4055
|
+
!s.includes("\u00A0") &&
|
|
4056
|
+
!s.includes("\n") &&
|
|
4057
|
+
!s.includes("\r")
|
|
4058
|
+
) {
|
|
4059
|
+
return s;
|
|
4060
|
+
}
|
|
4061
|
+
let out = "";
|
|
4062
|
+
let last = 0;
|
|
4063
|
+
for (let i = 0; i < s.length; i++) {
|
|
4064
|
+
let rep;
|
|
4065
|
+
switch (s.charCodeAt(i)) {
|
|
4066
|
+
case CC_AMPERSAND:
|
|
4067
|
+
rep = "&";
|
|
4068
|
+
break;
|
|
4069
|
+
case CC_LESS_THAN:
|
|
4070
|
+
rep = "<";
|
|
4071
|
+
break;
|
|
4072
|
+
case CC_GREATER_THAN:
|
|
4073
|
+
rep = ">";
|
|
4074
|
+
break;
|
|
4075
|
+
case CC_NO_BREAK_SPACE:
|
|
4076
|
+
rep = " ";
|
|
4077
|
+
break;
|
|
4078
|
+
case CC_LF:
|
|
4079
|
+
rep = " ";
|
|
4080
|
+
break;
|
|
4081
|
+
case CC_CR:
|
|
4082
|
+
rep = " ";
|
|
4083
|
+
break;
|
|
4084
|
+
default:
|
|
4085
|
+
continue;
|
|
4086
|
+
}
|
|
4087
|
+
out += s.slice(last, i) + rep;
|
|
4088
|
+
last = i + 1;
|
|
4089
|
+
}
|
|
4090
|
+
return out + s.slice(last);
|
|
3786
4091
|
};
|
|
3787
4092
|
|
|
3788
4093
|
// cspell:ignore advasoft altglyph altglyphdef altglyphitem animatecolor animatemotion animatetransform arcrole aswedit attributename attributetype basefrequency baseprofile bgsound calcmode clippathunits definitionurl diffuseconstant fedropshadow filterunits glyphref gradienttransform gradientunits hotjava hotmetal kernelmatrix kernelunitlength keypoints keysplines keytimes limitingconeangle malignmark markerheight markerwidth maskcontentunits maskunits metrius mglyph mtext numoctaves pathlength patterncontentunits patterntransform patternunits pointsatx pointsaty pointsatz preservealpha primitiveunits refx refy repeatcount repeatdur requiredextensions requiredfeatures selectedcontent silmaril softquad specularconstant specularexponent startoffset stddeviation stitchtiles surfacescale systemlanguage tablevalues targetx targety textlength viewbox viewtarget webtechs xchannelselector ychannelselector megamorphic attributeless rowspan imagesizes novalidate maxlength
|
|
3789
4094
|
|
|
3790
4095
|
// WHATWG HTML tree construction (https://html.spec.whatwg.org/multipage/parsing.html#tree-construction)
|
|
3791
|
-
// on top of
|
|
4096
|
+
// on top of tokenize. Scripting is always disabled (webpack is a build tool).
|
|
3792
4097
|
|
|
3793
4098
|
// Namespaces (mirrors swc_html_ast::Namespace)
|
|
3794
4099
|
const NS_HTML = 0;
|
|
@@ -3828,136 +4133,139 @@ const EMPTY_ATTRS = /** @type {AttributeRun} */ (
|
|
|
3828
4133
|
// lives in the four link columns (parent / firstChild / lastChild /
|
|
3829
4134
|
// nextSibling); the only heap references are the string payload / attribute
|
|
3830
4135
|
// name-and-value side arrays. Columns are module-level and reused
|
|
3831
|
-
// across parses (grown,
|
|
3832
|
-
// a steady-state parse allocates almost
|
|
3833
|
-
// read a tree before the next
|
|
3834
|
-
// "no node" so the link columns can use
|
|
3835
|
-
|
|
3836
|
-
let
|
|
4136
|
+
// across parses (grown, shrunk on release only past `_COLUMN_SHRINK_CAPACITY` —
|
|
4137
|
+
// the CSS parser's `_soa*` strategy), so a steady-state parse allocates almost
|
|
4138
|
+
// nothing per node; consumers must fully read a tree before the next
|
|
4139
|
+
// `parseHtml` call. Id 0 is reserved as "no node" so the link columns can use
|
|
4140
|
+
// 0 as null.
|
|
4141
|
+
let _nodeCapacity = 0;
|
|
4142
|
+
let _nodeCount = 0;
|
|
3837
4143
|
/** `NodeType` per node */
|
|
3838
|
-
let
|
|
4144
|
+
let _nodeTypes = new Uint8Array(0);
|
|
3839
4145
|
/** bits 0-1 namespace (`NS_*`), bit 2 self-closing (void element) */
|
|
3840
|
-
let
|
|
3841
|
-
let
|
|
3842
|
-
let
|
|
4146
|
+
let _nodeFlags = new Uint8Array(0);
|
|
4147
|
+
let _nodeStarts = new Int32Array(0);
|
|
4148
|
+
let _nodeEnds = new Int32Array(0);
|
|
3843
4149
|
/** end offset of an element's opening tag (after `>`) */
|
|
3844
|
-
let
|
|
4150
|
+
let _nodeTagEnds = new Int32Array(0);
|
|
3845
4151
|
/** end offset of an element's tag name */
|
|
3846
|
-
let
|
|
4152
|
+
let _nodeNameEnds = new Int32Array(0);
|
|
3847
4153
|
/** under `skip.text`, end offset of a raw-text element's body (`HtmlAstSkip`) */
|
|
3848
|
-
let
|
|
4154
|
+
let _nodeContentEnds = new Int32Array(0);
|
|
3849
4155
|
/** a `<template>`'s content DocumentFragment (0 = none) */
|
|
3850
|
-
let
|
|
3851
|
-
let
|
|
3852
|
-
let
|
|
3853
|
-
let
|
|
3854
|
-
let
|
|
4156
|
+
let _nodeTemplateContents = new Int32Array(0);
|
|
4157
|
+
let _nodeParents = new Int32Array(0);
|
|
4158
|
+
let _nodeFirstChildren = new Int32Array(0);
|
|
4159
|
+
let _nodeLastChildren = new Int32Array(0);
|
|
4160
|
+
let _nodeNextSiblings = new Int32Array(0);
|
|
3855
4161
|
/** @type {string[]} tag name / text data / comment data / doctype name */
|
|
3856
|
-
const
|
|
4162
|
+
const _nodeStrings = [];
|
|
3857
4163
|
/** first attribute id of an element's contiguous run */
|
|
3858
|
-
let
|
|
4164
|
+
let _nodeAttrStarts = new Int32Array(0);
|
|
3859
4165
|
/** attribute count of an element's run */
|
|
3860
|
-
let
|
|
4166
|
+
let _nodeAttrCounts = new Int32Array(0);
|
|
3861
4167
|
// The single doctype node's public/system ids (a document inserts at most one
|
|
3862
4168
|
// doctype node — later doctype tokens are ignored — so no column is needed).
|
|
3863
4169
|
/** @type {string | null} */
|
|
3864
|
-
let
|
|
4170
|
+
let _doctypePublicId = null;
|
|
3865
4171
|
/** @type {string | null} */
|
|
3866
|
-
let
|
|
4172
|
+
let _doctypeSystemId = null;
|
|
3867
4173
|
|
|
3868
4174
|
const NS_MASK = 3;
|
|
3869
4175
|
const FLAG_SELF_CLOSING = 4;
|
|
3870
4176
|
|
|
3871
4177
|
/** @type {(el: HtmlNodeRef) => string} */
|
|
3872
|
-
const
|
|
4178
|
+
const _tagNameOf = (el) => _nodeStrings[el];
|
|
3873
4179
|
/** @type {(el: HtmlNodeRef) => number} */
|
|
3874
|
-
const
|
|
4180
|
+
const _namespaceOf = (el) => _nodeFlags[el] & NS_MASK;
|
|
3875
4181
|
|
|
3876
4182
|
// === Attribute columns ===
|
|
3877
4183
|
// One attribute = one integer id into these columns; an element (and a
|
|
3878
4184
|
// start-tag token) holds a contiguous run. The value string is derived from
|
|
3879
|
-
// the source by offset on read — `
|
|
4185
|
+
// the source by offset on read — `_attrValues` carries an override only for
|
|
3880
4186
|
// valueless attributes (`""`) and offset-less adoption-agency clones — and the
|
|
3881
4187
|
// html5lib serializer name is derived from the adjusted name plus one flag
|
|
3882
4188
|
// bit, so per attribute only the interned name pointer is retained.
|
|
3883
|
-
let
|
|
3884
|
-
let
|
|
3885
|
-
let
|
|
3886
|
-
let
|
|
3887
|
-
let
|
|
3888
|
-
let
|
|
4189
|
+
let _attrCapacity = 0;
|
|
4190
|
+
let _attrCount = 0;
|
|
4191
|
+
let _attrNameStarts = new Int32Array(0);
|
|
4192
|
+
let _attrNameEnds = new Int32Array(0);
|
|
4193
|
+
let _attrValueStarts = new Int32Array(0);
|
|
4194
|
+
let _attrValueEnds = new Int32Array(0);
|
|
3889
4195
|
/** bit 0: name has a `FOREIGN_ATTR_NS` serializer name (set on foreign adjust) */
|
|
3890
|
-
let
|
|
4196
|
+
let _attrFlags = new Uint8Array(0);
|
|
3891
4197
|
/** @type {string[]} lowercased (foreign-content: adjusted) attribute name */
|
|
3892
|
-
const
|
|
4198
|
+
const _attrNames = [];
|
|
3893
4199
|
/** @type {(string | null)[]} value override (null = slice the source by offset) */
|
|
3894
|
-
const
|
|
4200
|
+
const _attrValues = [];
|
|
3895
4201
|
/** source of the current parse, for by-offset attribute values */
|
|
3896
|
-
let
|
|
4202
|
+
let _htmlSource = "";
|
|
3897
4203
|
|
|
3898
4204
|
/** @param {number} need minimum capacity */
|
|
3899
|
-
const
|
|
3900
|
-
let cap =
|
|
4205
|
+
const _growAttrColumns = (need) => {
|
|
4206
|
+
let cap = _attrCapacity || 4096;
|
|
3901
4207
|
while (cap < need) cap *= 2;
|
|
3902
4208
|
const nameStart = new Int32Array(cap);
|
|
3903
|
-
nameStart.set(
|
|
3904
|
-
|
|
4209
|
+
nameStart.set(_attrNameStarts);
|
|
4210
|
+
_attrNameStarts = nameStart;
|
|
3905
4211
|
const nameEnd = new Int32Array(cap);
|
|
3906
|
-
nameEnd.set(
|
|
3907
|
-
|
|
4212
|
+
nameEnd.set(_attrNameEnds);
|
|
4213
|
+
_attrNameEnds = nameEnd;
|
|
3908
4214
|
const valStart = new Int32Array(cap);
|
|
3909
|
-
valStart.set(
|
|
3910
|
-
|
|
4215
|
+
valStart.set(_attrValueStarts);
|
|
4216
|
+
_attrValueStarts = valStart;
|
|
3911
4217
|
const valEnd = new Int32Array(cap);
|
|
3912
|
-
valEnd.set(
|
|
3913
|
-
|
|
4218
|
+
valEnd.set(_attrValueEnds);
|
|
4219
|
+
_attrValueEnds = valEnd;
|
|
3914
4220
|
const fl = new Uint8Array(cap);
|
|
3915
|
-
fl.set(
|
|
3916
|
-
|
|
3917
|
-
|
|
4221
|
+
fl.set(_attrFlags);
|
|
4222
|
+
_attrFlags = fl;
|
|
4223
|
+
_attrCapacity = cap;
|
|
3918
4224
|
};
|
|
3919
4225
|
|
|
3920
4226
|
/** @type {(name: string, value: string | null, nameStart: number, nameEnd: number, valueStart: number, valueEnd: number) => number} */
|
|
3921
|
-
const
|
|
3922
|
-
const i = ++
|
|
3923
|
-
if (i >=
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
4227
|
+
const _allocAttr = (name, value, nameStart, nameEnd, valueStart, valueEnd) => {
|
|
4228
|
+
const i = ++_attrCount;
|
|
4229
|
+
if (i >= _attrCapacity) _growAttrColumns(i + 1);
|
|
4230
|
+
_attrNameStarts[i] = nameStart;
|
|
4231
|
+
_attrNameEnds[i] = nameEnd;
|
|
4232
|
+
_attrValueStarts[i] = valueStart;
|
|
4233
|
+
_attrValueEnds[i] = valueEnd;
|
|
4234
|
+
_attrFlags[i] = 0;
|
|
3929
4235
|
// Ids are sequential, so these indexed writes append (arrays stay packed).
|
|
3930
|
-
|
|
3931
|
-
|
|
4236
|
+
_attrNames[i] = name;
|
|
4237
|
+
_attrValues[i] = value;
|
|
3932
4238
|
return i;
|
|
3933
4239
|
};
|
|
3934
4240
|
|
|
3935
4241
|
/** @type {(i: number) => string} */
|
|
3936
|
-
const
|
|
3937
|
-
const v =
|
|
3938
|
-
return v !== null
|
|
4242
|
+
const _attrValueOf = (i) => {
|
|
4243
|
+
const v = _attrValues[i];
|
|
4244
|
+
return v !== null
|
|
4245
|
+
? v
|
|
4246
|
+
: _htmlSource.slice(_attrValueStarts[i], _attrValueEnds[i]);
|
|
3939
4247
|
};
|
|
3940
4248
|
|
|
3941
4249
|
// Linear name lookup in a run — attribute lists are short, a loop beats a Map.
|
|
3942
4250
|
/** @type {(start: number, count: number, name: string) => number} */
|
|
3943
|
-
const
|
|
4251
|
+
const _findAttr = (start, count, name) => {
|
|
3944
4252
|
for (let i = start; i < start + count; i++) {
|
|
3945
|
-
if (
|
|
4253
|
+
if (_attrNames[i] === name) return i;
|
|
3946
4254
|
}
|
|
3947
4255
|
return 0;
|
|
3948
4256
|
};
|
|
3949
4257
|
|
|
3950
4258
|
/** @type {(i: number) => number} exact copy of an attribute into a new id */
|
|
3951
|
-
const
|
|
3952
|
-
const c =
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
4259
|
+
const _copyAttr = (i) => {
|
|
4260
|
+
const c = _allocAttr(
|
|
4261
|
+
_attrNames[i],
|
|
4262
|
+
_attrValues[i],
|
|
4263
|
+
_attrNameStarts[i],
|
|
4264
|
+
_attrNameEnds[i],
|
|
4265
|
+
_attrValueStarts[i],
|
|
4266
|
+
_attrValueEnds[i]
|
|
3959
4267
|
);
|
|
3960
|
-
|
|
4268
|
+
_attrFlags[c] = _attrFlags[i];
|
|
3961
4269
|
return c;
|
|
3962
4270
|
};
|
|
3963
4271
|
|
|
@@ -3966,135 +4274,167 @@ const _aCopy = (i) => {
|
|
|
3966
4274
|
// an uppercase letter (unadjusted names are always lowercased), serializing as
|
|
3967
4275
|
// itself. Everything else serializes as the plain name (undefined here).
|
|
3968
4276
|
/** @type {(i: number) => string | undefined} */
|
|
3969
|
-
const
|
|
3970
|
-
if ((
|
|
3971
|
-
const name =
|
|
4277
|
+
const _attrSerializedName = (i) => {
|
|
4278
|
+
if ((_attrFlags[i] & 1) !== 0) return FOREIGN_ATTR_NS[_attrNames[i]];
|
|
4279
|
+
const name = _attrNames[i];
|
|
3972
4280
|
return /[A-Z]/.test(name) ? name : undefined;
|
|
3973
4281
|
};
|
|
3974
4282
|
|
|
3975
4283
|
/** @param {number} need minimum capacity */
|
|
3976
|
-
const
|
|
3977
|
-
let cap =
|
|
4284
|
+
const _growNodeColumns = (need) => {
|
|
4285
|
+
let cap = _nodeCapacity || 4096;
|
|
3978
4286
|
while (cap < need) cap *= 2;
|
|
3979
4287
|
const ty = new Uint8Array(cap);
|
|
3980
|
-
ty.set(
|
|
3981
|
-
|
|
4288
|
+
ty.set(_nodeTypes);
|
|
4289
|
+
_nodeTypes = ty;
|
|
3982
4290
|
const fl = new Uint8Array(cap);
|
|
3983
|
-
fl.set(
|
|
3984
|
-
|
|
4291
|
+
fl.set(_nodeFlags);
|
|
4292
|
+
_nodeFlags = fl;
|
|
3985
4293
|
const st = new Int32Array(cap);
|
|
3986
|
-
st.set(
|
|
3987
|
-
|
|
4294
|
+
st.set(_nodeStarts);
|
|
4295
|
+
_nodeStarts = st;
|
|
3988
4296
|
const en = new Int32Array(cap);
|
|
3989
|
-
en.set(
|
|
3990
|
-
|
|
4297
|
+
en.set(_nodeEnds);
|
|
4298
|
+
_nodeEnds = en;
|
|
3991
4299
|
const tagEnd = new Int32Array(cap);
|
|
3992
|
-
tagEnd.set(
|
|
3993
|
-
|
|
4300
|
+
tagEnd.set(_nodeTagEnds);
|
|
4301
|
+
_nodeTagEnds = tagEnd;
|
|
3994
4302
|
const nameEnd = new Int32Array(cap);
|
|
3995
|
-
nameEnd.set(
|
|
3996
|
-
|
|
4303
|
+
nameEnd.set(_nodeNameEnds);
|
|
4304
|
+
_nodeNameEnds = nameEnd;
|
|
3997
4305
|
const cEnd = new Int32Array(cap);
|
|
3998
|
-
cEnd.set(
|
|
3999
|
-
|
|
4306
|
+
cEnd.set(_nodeContentEnds);
|
|
4307
|
+
_nodeContentEnds = cEnd;
|
|
4000
4308
|
const tc = new Int32Array(cap);
|
|
4001
|
-
tc.set(
|
|
4002
|
-
|
|
4309
|
+
tc.set(_nodeTemplateContents);
|
|
4310
|
+
_nodeTemplateContents = tc;
|
|
4003
4311
|
const parent = new Int32Array(cap);
|
|
4004
|
-
parent.set(
|
|
4005
|
-
|
|
4312
|
+
parent.set(_nodeParents);
|
|
4313
|
+
_nodeParents = parent;
|
|
4006
4314
|
const first = new Int32Array(cap);
|
|
4007
|
-
first.set(
|
|
4008
|
-
|
|
4315
|
+
first.set(_nodeFirstChildren);
|
|
4316
|
+
_nodeFirstChildren = first;
|
|
4009
4317
|
const last = new Int32Array(cap);
|
|
4010
|
-
last.set(
|
|
4011
|
-
|
|
4318
|
+
last.set(_nodeLastChildren);
|
|
4319
|
+
_nodeLastChildren = last;
|
|
4012
4320
|
const next = new Int32Array(cap);
|
|
4013
|
-
next.set(
|
|
4014
|
-
|
|
4321
|
+
next.set(_nodeNextSiblings);
|
|
4322
|
+
_nodeNextSiblings = next;
|
|
4015
4323
|
const aStart = new Int32Array(cap);
|
|
4016
|
-
aStart.set(
|
|
4017
|
-
|
|
4324
|
+
aStart.set(_nodeAttrStarts);
|
|
4325
|
+
_nodeAttrStarts = aStart;
|
|
4018
4326
|
const aCount = new Int32Array(cap);
|
|
4019
|
-
aCount.set(
|
|
4020
|
-
|
|
4021
|
-
|
|
4327
|
+
aCount.set(_nodeAttrCounts);
|
|
4328
|
+
_nodeAttrCounts = aCount;
|
|
4329
|
+
_nodeCapacity = cap;
|
|
4022
4330
|
};
|
|
4023
4331
|
|
|
4024
4332
|
/** Start a new parse: invalidate all prior refs, release prior heap refs. */
|
|
4025
|
-
const
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4333
|
+
const _resetAstColumns = () => {
|
|
4334
|
+
_nodeCount = 0;
|
|
4335
|
+
_attrCount = 0;
|
|
4336
|
+
_nodeStrings.length = 0;
|
|
4337
|
+
_attrNames.length = 0;
|
|
4338
|
+
_attrValues.length = 0;
|
|
4031
4339
|
// Keep id 0 ("no node" / "no attribute") occupied so writes stay packed.
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4340
|
+
_nodeStrings.push("");
|
|
4341
|
+
_attrNames.push("");
|
|
4342
|
+
_attrValues.push(null);
|
|
4343
|
+
_doctypePublicId = null;
|
|
4344
|
+
_doctypeSystemId = null;
|
|
4037
4345
|
};
|
|
4038
4346
|
|
|
4347
|
+
// The typed-array columns grow to the largest document ever parsed; above this
|
|
4348
|
+
// capacity they are re-shrunk on release so one pathological file can't pin
|
|
4349
|
+
// tens of MB at module level for the process lifetime (~50 bytes/node,
|
|
4350
|
+
// ~17 bytes/attribute of capacity across the columns).
|
|
4351
|
+
const _COLUMN_SHRINK_CAPACITY = 65536;
|
|
4352
|
+
|
|
4039
4353
|
// Release the side arrays' heap references (strings, attribute names/values)
|
|
4040
4354
|
// once a walk has consumed the tree, so the retained columns don't pin the
|
|
4041
4355
|
// parsed source until the next parse.
|
|
4042
|
-
const
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4356
|
+
const _releaseAstColumns = () => {
|
|
4357
|
+
_nodeCount = 0;
|
|
4358
|
+
_attrCount = 0;
|
|
4359
|
+
_nodeStrings.length = 0;
|
|
4360
|
+
_attrNames.length = 0;
|
|
4361
|
+
_attrValues.length = 0;
|
|
4362
|
+
_htmlSource = "";
|
|
4363
|
+
_doctypePublicId = null;
|
|
4364
|
+
_doctypeSystemId = null;
|
|
4365
|
+
if (_nodeCapacity > _COLUMN_SHRINK_CAPACITY) {
|
|
4366
|
+
_nodeCapacity = 0;
|
|
4367
|
+
_nodeTypes = new Uint8Array(0);
|
|
4368
|
+
_nodeFlags = new Uint8Array(0);
|
|
4369
|
+
_nodeStarts = new Int32Array(0);
|
|
4370
|
+
_nodeEnds = new Int32Array(0);
|
|
4371
|
+
_nodeTagEnds = new Int32Array(0);
|
|
4372
|
+
_nodeNameEnds = new Int32Array(0);
|
|
4373
|
+
_nodeContentEnds = new Int32Array(0);
|
|
4374
|
+
_nodeTemplateContents = new Int32Array(0);
|
|
4375
|
+
_nodeParents = new Int32Array(0);
|
|
4376
|
+
_nodeFirstChildren = new Int32Array(0);
|
|
4377
|
+
_nodeLastChildren = new Int32Array(0);
|
|
4378
|
+
_nodeNextSiblings = new Int32Array(0);
|
|
4379
|
+
_nodeAttrStarts = new Int32Array(0);
|
|
4380
|
+
_nodeAttrCounts = new Int32Array(0);
|
|
4381
|
+
}
|
|
4382
|
+
if (_attrCapacity > _COLUMN_SHRINK_CAPACITY) {
|
|
4383
|
+
_attrCapacity = 0;
|
|
4384
|
+
_attrNameStarts = new Int32Array(0);
|
|
4385
|
+
_attrNameEnds = new Int32Array(0);
|
|
4386
|
+
_attrValueStarts = new Int32Array(0);
|
|
4387
|
+
_attrValueEnds = new Int32Array(0);
|
|
4388
|
+
_attrFlags = new Uint8Array(0);
|
|
4389
|
+
}
|
|
4051
4390
|
};
|
|
4052
4391
|
|
|
4053
4392
|
/** @type {(type: number, start: number, end: number) => HtmlNodeRef} */
|
|
4054
|
-
const
|
|
4055
|
-
const i = ++
|
|
4056
|
-
if (i >=
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
//
|
|
4072
|
-
|
|
4393
|
+
const _allocNode = (type, start, end) => {
|
|
4394
|
+
const i = ++_nodeCount;
|
|
4395
|
+
if (i >= _nodeCapacity) _growNodeColumns(i + 1);
|
|
4396
|
+
_nodeTypes[i] = type;
|
|
4397
|
+
_nodeFlags[i] = 0;
|
|
4398
|
+
_nodeStarts[i] = start;
|
|
4399
|
+
_nodeEnds[i] = end;
|
|
4400
|
+
_nodeTagEnds[i] = 0;
|
|
4401
|
+
_nodeNameEnds[i] = 0;
|
|
4402
|
+
_nodeContentEnds[i] = 0;
|
|
4403
|
+
_nodeTemplateContents[i] = 0;
|
|
4404
|
+
_nodeParents[i] = 0;
|
|
4405
|
+
_nodeFirstChildren[i] = 0;
|
|
4406
|
+
_nodeLastChildren[i] = 0;
|
|
4407
|
+
_nodeNextSiblings[i] = 0;
|
|
4408
|
+
_nodeAttrStarts[i] = 0;
|
|
4409
|
+
_nodeAttrCounts[i] = 0;
|
|
4410
|
+
// `_nodeStrings[i]` is written by every caller right after this returns
|
|
4411
|
+
// (tag name / text / comment data; "" at the few string-less sites), as a
|
|
4412
|
+
// packed sequential append — no placeholder double-write here.
|
|
4073
4413
|
return i;
|
|
4074
4414
|
};
|
|
4075
4415
|
|
|
4076
4416
|
// Raw child append — no text merging, no `<template>` content redirect (the
|
|
4077
4417
|
// tree builder layers those on top). `node` must be detached (`next` = 0).
|
|
4078
4418
|
/** @type {(parent: HtmlNodeRef, node: HtmlNodeRef) => void} */
|
|
4079
|
-
const
|
|
4080
|
-
|
|
4081
|
-
const last =
|
|
4082
|
-
if (last === 0)
|
|
4083
|
-
else
|
|
4084
|
-
|
|
4419
|
+
const _appendChild = (parent, node) => {
|
|
4420
|
+
_nodeParents[node] = parent;
|
|
4421
|
+
const last = _nodeLastChildren[parent];
|
|
4422
|
+
if (last === 0) _nodeFirstChildren[parent] = node;
|
|
4423
|
+
else _nodeNextSiblings[last] = node;
|
|
4424
|
+
_nodeLastChildren[parent] = node;
|
|
4085
4425
|
};
|
|
4086
4426
|
|
|
4087
4427
|
/** @type {(data: string, start: number, end: number) => HtmlNodeRef} */
|
|
4088
|
-
const
|
|
4089
|
-
const i =
|
|
4090
|
-
|
|
4428
|
+
const _makeTextNode = (data, start, end) => {
|
|
4429
|
+
const i = _allocNode(NodeType.Text, start, end);
|
|
4430
|
+
_nodeStrings[i] = data;
|
|
4091
4431
|
return i;
|
|
4092
4432
|
};
|
|
4093
4433
|
|
|
4094
4434
|
/** @type {(data: string, start: number, end: number) => HtmlNodeRef} */
|
|
4095
|
-
const
|
|
4096
|
-
const i =
|
|
4097
|
-
|
|
4435
|
+
const _makeCommentNode = (data, start, end) => {
|
|
4436
|
+
const i = _allocNode(NodeType.Comment, start, end);
|
|
4437
|
+
_nodeStrings[i] = data;
|
|
4098
4438
|
return i;
|
|
4099
4439
|
};
|
|
4100
4440
|
|
|
@@ -4106,12 +4446,12 @@ const AFE_MARKER = -1;
|
|
|
4106
4446
|
// dependency for the reopened element's spans. Values are materialized since
|
|
4107
4447
|
// the offsets are gone.
|
|
4108
4448
|
const cloneAttrs = (/** @type {HtmlElement} */ el) => {
|
|
4109
|
-
const start =
|
|
4110
|
-
const count =
|
|
4111
|
-
const newStart =
|
|
4449
|
+
const start = _nodeAttrStarts[el];
|
|
4450
|
+
const count = _nodeAttrCounts[el];
|
|
4451
|
+
const newStart = _attrCount + 1;
|
|
4112
4452
|
for (let i = start; i < start + count; i++) {
|
|
4113
|
-
const c =
|
|
4114
|
-
|
|
4453
|
+
const c = _allocAttr(_attrNames[i], _attrValueOf(i), -1, -1, -1, -1);
|
|
4454
|
+
_attrFlags[c] = _attrFlags[i];
|
|
4115
4455
|
}
|
|
4116
4456
|
return { start: newStart, count };
|
|
4117
4457
|
};
|
|
@@ -4124,20 +4464,20 @@ const mergeAttrs = (
|
|
|
4124
4464
|
/** @type {HtmlElement} */ el,
|
|
4125
4465
|
/** @type {AttributeRun} */ run
|
|
4126
4466
|
) => {
|
|
4127
|
-
const start =
|
|
4128
|
-
const count =
|
|
4467
|
+
const start = _nodeAttrStarts[el];
|
|
4468
|
+
const count = _nodeAttrCounts[el];
|
|
4129
4469
|
let extra = 0;
|
|
4130
4470
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
4131
|
-
if (
|
|
4471
|
+
if (_findAttr(start, count, _attrNames[i]) === 0) extra++;
|
|
4132
4472
|
}
|
|
4133
4473
|
if (extra === 0) return;
|
|
4134
|
-
const newStart =
|
|
4135
|
-
for (let i = start; i < start + count; i++)
|
|
4474
|
+
const newStart = _attrCount + 1;
|
|
4475
|
+
for (let i = start; i < start + count; i++) _copyAttr(i);
|
|
4136
4476
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
4137
|
-
if (
|
|
4477
|
+
if (_findAttr(start, count, _attrNames[i]) === 0) _copyAttr(i);
|
|
4138
4478
|
}
|
|
4139
|
-
|
|
4140
|
-
|
|
4479
|
+
_nodeAttrStarts[el] = newStart;
|
|
4480
|
+
_nodeAttrCounts[el] = count + extra;
|
|
4141
4481
|
};
|
|
4142
4482
|
|
|
4143
4483
|
/**
|
|
@@ -4157,7 +4497,7 @@ const mergeAttrs = (
|
|
|
4157
4497
|
/**
|
|
4158
4498
|
* A node reference into the struct-of-arrays AST: an integer id indexing the
|
|
4159
4499
|
* parallel `_h*` columns. Read fields through the exported accessor `A`. Refs
|
|
4160
|
-
* are only valid until the next `
|
|
4500
|
+
* are only valid until the next `parseHtml` call — the columns are reused
|
|
4161
4501
|
* across parses — so consume a tree fully before parsing again.
|
|
4162
4502
|
* @typedef {number} HtmlNodeRef
|
|
4163
4503
|
*/
|
|
@@ -4375,8 +4715,8 @@ const FORMATTING = new Set([
|
|
|
4375
4715
|
const HEADING = new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
|
|
4376
4716
|
|
|
4377
4717
|
const isSpecial = (/** @type {HtmlElement} */ el) => {
|
|
4378
|
-
const ns =
|
|
4379
|
-
const tag =
|
|
4718
|
+
const ns = _namespaceOf(el);
|
|
4719
|
+
const tag = _tagNameOf(el);
|
|
4380
4720
|
if (ns === NS_HTML) return SPECIAL.has(tag);
|
|
4381
4721
|
if (ns === NS_MATHML) return MATHML_SPECIAL.has(tag);
|
|
4382
4722
|
if (ns === NS_SVG) return SVG_SPECIAL.has(tag.toLowerCase());
|
|
@@ -4830,7 +5170,7 @@ const FONT_BREAKOUT_ATTRS = new Set(["color", "face", "size"]);
|
|
|
4830
5170
|
// `<font color|face|size>` breaks out of foreign content (§13.2.6.5).
|
|
4831
5171
|
const hasFontBreakoutAttr = (/** @type {AttributeRun} */ run) => {
|
|
4832
5172
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
4833
|
-
if (FONT_BREAKOUT_ATTRS.has(
|
|
5173
|
+
if (FONT_BREAKOUT_ATTRS.has(_attrNames[i])) return true;
|
|
4834
5174
|
}
|
|
4835
5175
|
return false;
|
|
4836
5176
|
};
|
|
@@ -4922,10 +5262,10 @@ const internLowerName = (table, input, start, end) => {
|
|
|
4922
5262
|
if (hit === undefined) break;
|
|
4923
5263
|
if (hashes[slot] === h) {
|
|
4924
5264
|
if (typeof hit === "string") {
|
|
4925
|
-
if (
|
|
5265
|
+
if (rangeEqualsLowerCase(input, start, end, hit)) return hit;
|
|
4926
5266
|
} else {
|
|
4927
5267
|
for (let i = 0; i < hit.length; i++) {
|
|
4928
|
-
if (
|
|
5268
|
+
if (rangeEqualsLowerCase(input, start, end, hit[i])) return hit[i];
|
|
4929
5269
|
}
|
|
4930
5270
|
}
|
|
4931
5271
|
break;
|
|
@@ -4969,7 +5309,7 @@ const ATTR_NAME_INTERN = buildNameInternTable(
|
|
|
4969
5309
|
// Hoisted so the many `open.some(...)` "is there an open HTML <template>?"
|
|
4970
5310
|
// checks reuse one predicate instead of allocating an arrow per call.
|
|
4971
5311
|
const isHtmlTemplateEl = (/** @type {HtmlElement} */ e) =>
|
|
4972
|
-
|
|
5312
|
+
_tagNameOf(e) === "template" && _namespaceOf(e) === NS_HTML;
|
|
4973
5313
|
|
|
4974
5314
|
// Raw text / escapable raw text elements (WHATWG §13.1.2) plus the other
|
|
4975
5315
|
// RAWTEXT/RCDATA/PLAINTEXT-tokenized elements. Under `skip.text` their body's
|
|
@@ -4988,7 +5328,10 @@ const RAW_TEXT_ELEMENTS = new Set([
|
|
|
4988
5328
|
"plaintext"
|
|
4989
5329
|
]);
|
|
4990
5330
|
|
|
4991
|
-
/**
|
|
5331
|
+
/**
|
|
5332
|
+
* Shared empty skip set so the common (no-skip) call allocates nothing.
|
|
5333
|
+
* @type {HtmlAstSkip}
|
|
5334
|
+
*/
|
|
4992
5335
|
const EMPTY_SKIP = Object.freeze({});
|
|
4993
5336
|
|
|
4994
5337
|
/**
|
|
@@ -5002,18 +5345,40 @@ const EMPTY_SKIP = Object.freeze({});
|
|
|
5002
5345
|
*/
|
|
5003
5346
|
|
|
5004
5347
|
/**
|
|
5005
|
-
* @
|
|
5006
|
-
* @
|
|
5007
|
-
* @
|
|
5348
|
+
* @typedef {object} HtmlParseOptions
|
|
5349
|
+
* @property {string=} fragmentContext context element name for fragment parsing (e.g. `td`, `svg path`); omit for a full document
|
|
5350
|
+
* @property {HtmlAstSkip=} skip node kinds to omit from the AST (see `HtmlAstSkip`); omit to build the full tree
|
|
5351
|
+
*/
|
|
5352
|
+
|
|
5353
|
+
/**
|
|
5354
|
+
* @param {string} input HTML source
|
|
5355
|
+
* @param {number=} pos start byte offset (string input only; default `0`)
|
|
5356
|
+
* @param {HtmlParseOptions=} options parse options (fragment context, AST skips)
|
|
5008
5357
|
* @returns {HtmlDocument} ref to the document node — read through `A`; valid until the next parse
|
|
5009
5358
|
*/
|
|
5010
|
-
const
|
|
5359
|
+
const parseHtml = (input, pos = 0, options = {}) => {
|
|
5360
|
+
const source = input;
|
|
5361
|
+
const { fragmentContext, skip = EMPTY_SKIP } = options;
|
|
5011
5362
|
const skipText = skip.text === true;
|
|
5012
5363
|
const skipComments = skip.comments === true;
|
|
5013
5364
|
const skipDoctype = skip.doctype === true;
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5365
|
+
_resetAstColumns();
|
|
5366
|
+
// Pre-size the columns from the input length: a document big enough to
|
|
5367
|
+
// exceed `_COLUMN_SHRINK_CAPACITY` re-enters with released (empty) columns,
|
|
5368
|
+
// and growing through the doubling cascade re-allocates and copies every
|
|
5369
|
+
// column several times per parse. ~12 bytes/node and ~50 bytes/attribute
|
|
5370
|
+
// are the densest realistic HTML, so `len/12` (`len/48`) reaches the final
|
|
5371
|
+
// capacity in one allocation; a sparser document over-allocates bounded
|
|
5372
|
+
// scratch that the post-walk release frees, and the doubling growth remains
|
|
5373
|
+
// as the safety net when the estimate is short.
|
|
5374
|
+
const _sizeEstimate = (input.length / 12) | 0;
|
|
5375
|
+
if (_sizeEstimate > _nodeCapacity) _growNodeColumns(_sizeEstimate);
|
|
5376
|
+
if (_sizeEstimate > _attrCapacity << 2) {
|
|
5377
|
+
_growAttrColumns(_sizeEstimate >> 2);
|
|
5378
|
+
}
|
|
5379
|
+
_htmlSource = source;
|
|
5380
|
+
const doc = _allocNode(NodeType.Document, 0, 0);
|
|
5381
|
+
_nodeStrings[doc] = "";
|
|
5017
5382
|
let mode = MODE_INITIAL;
|
|
5018
5383
|
// Mode to return to after MODE_TEXT / MODE_IN_TABLE_TEXT (0 = unset).
|
|
5019
5384
|
let originalMode = 0;
|
|
@@ -5049,24 +5414,24 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5049
5414
|
/** @type {AttributeRun} */ attrs,
|
|
5050
5415
|
/** @type {TagPos | null | undefined} */ pos
|
|
5051
5416
|
) => {
|
|
5052
|
-
const el =
|
|
5417
|
+
const el = _allocNode(
|
|
5053
5418
|
NodeType.Element,
|
|
5054
5419
|
pos ? pos.start : 0,
|
|
5055
5420
|
pos ? pos.end : 0
|
|
5056
5421
|
);
|
|
5057
5422
|
// Void HTML elements are marked self-closing and never receive children.
|
|
5058
|
-
|
|
5423
|
+
_nodeFlags[el] =
|
|
5059
5424
|
ns === NS_HTML && VOID.has(tagName) ? ns | FLAG_SELF_CLOSING : ns;
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5425
|
+
_nodeStrings[el] = tagName;
|
|
5426
|
+
_nodeAttrStarts[el] = attrs.start;
|
|
5427
|
+
_nodeAttrCounts[el] = attrs.count;
|
|
5063
5428
|
if (pos) {
|
|
5064
|
-
|
|
5065
|
-
|
|
5429
|
+
_nodeTagEnds[el] = pos.tagEnd;
|
|
5430
|
+
_nodeNameEnds[el] = pos.nameEnd;
|
|
5066
5431
|
// End of a raw-text element's body under `skip.text` (defaults to the
|
|
5067
5432
|
// body start, i.e. empty); lets consumers read `<script>`/`<style>`
|
|
5068
5433
|
// content as [`tagEnd`, `contentEnd`] without a `Text` node.
|
|
5069
|
-
|
|
5434
|
+
_nodeContentEnds[el] = pos.tagEnd;
|
|
5070
5435
|
}
|
|
5071
5436
|
return el;
|
|
5072
5437
|
};
|
|
@@ -5078,7 +5443,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5078
5443
|
* @returns {HtmlNodeRef} effective container to link children into
|
|
5079
5444
|
*/
|
|
5080
5445
|
const effParent = (parent) => {
|
|
5081
|
-
const tc =
|
|
5446
|
+
const tc = _nodeTemplateContents[parent];
|
|
5082
5447
|
return tc !== 0 ? tc : parent;
|
|
5083
5448
|
};
|
|
5084
5449
|
|
|
@@ -5087,17 +5452,17 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5087
5452
|
/** @type {HtmlNodeRef} */ node
|
|
5088
5453
|
) => {
|
|
5089
5454
|
const p = effParent(parent);
|
|
5090
|
-
const last =
|
|
5455
|
+
const last = _nodeLastChildren[p];
|
|
5091
5456
|
if (
|
|
5092
|
-
|
|
5457
|
+
_nodeTypes[node] === NodeType.Text &&
|
|
5093
5458
|
last !== 0 &&
|
|
5094
|
-
|
|
5459
|
+
_nodeTypes[last] === NodeType.Text
|
|
5095
5460
|
) {
|
|
5096
|
-
|
|
5097
|
-
|
|
5461
|
+
_nodeStrings[last] += _nodeStrings[node];
|
|
5462
|
+
_nodeEnds[last] = _nodeEnds[node];
|
|
5098
5463
|
return;
|
|
5099
5464
|
}
|
|
5100
|
-
|
|
5465
|
+
_appendChild(p, node);
|
|
5101
5466
|
};
|
|
5102
5467
|
|
|
5103
5468
|
// Reused result of `appropriatePlace` — consumed synchronously by
|
|
@@ -5119,23 +5484,23 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5119
5484
|
const target = cur();
|
|
5120
5485
|
if (
|
|
5121
5486
|
fosterParenting &&
|
|
5122
|
-
TABLE_CONTEXT.has(
|
|
5123
|
-
|
|
5487
|
+
TABLE_CONTEXT.has(_tagNameOf(target)) &&
|
|
5488
|
+
_namespaceOf(target) === NS_HTML
|
|
5124
5489
|
) {
|
|
5125
5490
|
// find last template / last table
|
|
5126
5491
|
let lastTemplate = -1;
|
|
5127
5492
|
let lastTable = -1;
|
|
5128
5493
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
5129
5494
|
if (
|
|
5130
|
-
|
|
5131
|
-
|
|
5495
|
+
_tagNameOf(open[i]) === "template" &&
|
|
5496
|
+
_namespaceOf(open[i]) === NS_HTML &&
|
|
5132
5497
|
lastTemplate === -1
|
|
5133
5498
|
) {
|
|
5134
5499
|
lastTemplate = i;
|
|
5135
5500
|
}
|
|
5136
5501
|
if (
|
|
5137
|
-
|
|
5138
|
-
|
|
5502
|
+
_tagNameOf(open[i]) === "table" &&
|
|
5503
|
+
_namespaceOf(open[i]) === NS_HTML &&
|
|
5139
5504
|
lastTable === -1
|
|
5140
5505
|
) {
|
|
5141
5506
|
lastTable = i;
|
|
@@ -5151,7 +5516,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5151
5516
|
return placeAt(open[0], 0);
|
|
5152
5517
|
}
|
|
5153
5518
|
const table = open[lastTable];
|
|
5154
|
-
const tp =
|
|
5519
|
+
const tp = _nodeParents[table];
|
|
5155
5520
|
if (tp !== 0) return placeAt(tp, table);
|
|
5156
5521
|
return placeAt(open[lastTable - 1], 0);
|
|
5157
5522
|
}
|
|
@@ -5168,10 +5533,10 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5168
5533
|
// Find `before`'s previous sibling (insert-before is a rare foster/
|
|
5169
5534
|
// adoption path, so the sibling scan stays off the hot path).
|
|
5170
5535
|
let prev = 0;
|
|
5171
|
-
let c =
|
|
5536
|
+
let c = _nodeFirstChildren[p];
|
|
5172
5537
|
while (c !== 0 && c !== before) {
|
|
5173
5538
|
prev = c;
|
|
5174
|
-
c =
|
|
5539
|
+
c = _nodeNextSiblings[c];
|
|
5175
5540
|
}
|
|
5176
5541
|
if (c === 0) {
|
|
5177
5542
|
// `before` not under `parent` (not reachable from the spec paths).
|
|
@@ -5179,18 +5544,18 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5179
5544
|
return;
|
|
5180
5545
|
}
|
|
5181
5546
|
if (
|
|
5182
|
-
|
|
5547
|
+
_nodeTypes[node] === NodeType.Text &&
|
|
5183
5548
|
prev !== 0 &&
|
|
5184
|
-
|
|
5549
|
+
_nodeTypes[prev] === NodeType.Text
|
|
5185
5550
|
) {
|
|
5186
5551
|
// Before-node merge deliberately does not bump the sibling's `end`.
|
|
5187
|
-
|
|
5552
|
+
_nodeStrings[prev] += _nodeStrings[node];
|
|
5188
5553
|
return;
|
|
5189
5554
|
}
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
if (prev === 0)
|
|
5193
|
-
else
|
|
5555
|
+
_nodeParents[node] = p;
|
|
5556
|
+
_nodeNextSiblings[node] = before;
|
|
5557
|
+
if (prev === 0) _nodeFirstChildren[p] = node;
|
|
5558
|
+
else _nodeNextSiblings[prev] = node;
|
|
5194
5559
|
} else {
|
|
5195
5560
|
appendTo(place.parent, node);
|
|
5196
5561
|
}
|
|
@@ -5202,7 +5567,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5202
5567
|
/** @type {number} */ end
|
|
5203
5568
|
) => {
|
|
5204
5569
|
const place = appropriatePlace();
|
|
5205
|
-
if (
|
|
5570
|
+
if (_nodeTypes[place.parent] === NodeType.Document) return; // never insert text into document
|
|
5206
5571
|
// `skip.text`: drop every `Text` node — construction already used the
|
|
5207
5572
|
// decoded token, so removing the node never affects element structure.
|
|
5208
5573
|
// For a raw-text element record the body end so a consumer reads the span
|
|
@@ -5211,8 +5576,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5211
5576
|
// foreign content (e.g. SVG `<style>`) too.
|
|
5212
5577
|
if (skipText) {
|
|
5213
5578
|
const p = place.parent;
|
|
5214
|
-
if (
|
|
5215
|
-
|
|
5579
|
+
if (
|
|
5580
|
+
_nodeTypes[p] === NodeType.Element &&
|
|
5581
|
+
RAW_TEXT_ELEMENTS.has(_tagNameOf(p))
|
|
5582
|
+
) {
|
|
5583
|
+
_nodeContentEnds[p] = end;
|
|
5216
5584
|
}
|
|
5217
5585
|
return;
|
|
5218
5586
|
}
|
|
@@ -5224,33 +5592,33 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5224
5592
|
const before = place.beforeNode;
|
|
5225
5593
|
if (before !== 0) {
|
|
5226
5594
|
let prev = 0;
|
|
5227
|
-
let c =
|
|
5595
|
+
let c = _nodeFirstChildren[p];
|
|
5228
5596
|
while (c !== 0 && c !== before) {
|
|
5229
5597
|
prev = c;
|
|
5230
|
-
c =
|
|
5598
|
+
c = _nodeNextSiblings[c];
|
|
5231
5599
|
}
|
|
5232
5600
|
if (c === 0) {
|
|
5233
5601
|
// `before` not under `parent` (not reachable from the spec paths).
|
|
5234
|
-
|
|
5602
|
+
_appendChild(p, _makeTextNode(data, start, end));
|
|
5235
5603
|
return;
|
|
5236
5604
|
}
|
|
5237
|
-
if (prev !== 0 &&
|
|
5238
|
-
|
|
5605
|
+
if (prev !== 0 && _nodeTypes[prev] === NodeType.Text) {
|
|
5606
|
+
_nodeStrings[prev] += data;
|
|
5239
5607
|
return;
|
|
5240
5608
|
}
|
|
5241
|
-
const node =
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
if (prev === 0)
|
|
5245
|
-
else
|
|
5609
|
+
const node = _makeTextNode(data, start, end);
|
|
5610
|
+
_nodeParents[node] = p;
|
|
5611
|
+
_nodeNextSiblings[node] = before;
|
|
5612
|
+
if (prev === 0) _nodeFirstChildren[p] = node;
|
|
5613
|
+
else _nodeNextSiblings[prev] = node;
|
|
5246
5614
|
} else {
|
|
5247
|
-
const last =
|
|
5248
|
-
if (last !== 0 &&
|
|
5249
|
-
|
|
5250
|
-
|
|
5615
|
+
const last = _nodeLastChildren[p];
|
|
5616
|
+
if (last !== 0 && _nodeTypes[last] === NodeType.Text) {
|
|
5617
|
+
_nodeStrings[last] += data;
|
|
5618
|
+
_nodeEnds[last] = end;
|
|
5251
5619
|
return;
|
|
5252
5620
|
}
|
|
5253
|
-
|
|
5621
|
+
_appendChild(p, _makeTextNode(data, start, end));
|
|
5254
5622
|
}
|
|
5255
5623
|
};
|
|
5256
5624
|
|
|
@@ -5263,7 +5631,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5263
5631
|
const insertComment = (data, start, end, place) => {
|
|
5264
5632
|
if (skipComments) return;
|
|
5265
5633
|
const p = place || appropriatePlace();
|
|
5266
|
-
insertAtPlace(p,
|
|
5634
|
+
insertAtPlace(p, _makeCommentNode(data, start, end));
|
|
5267
5635
|
};
|
|
5268
5636
|
|
|
5269
5637
|
const insertHtmlElement = (
|
|
@@ -5293,10 +5661,12 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5293
5661
|
|
|
5294
5662
|
// ---- scopes ----
|
|
5295
5663
|
const isScopeBoundary = (/** @type {HtmlElement} */ el) => {
|
|
5296
|
-
if (
|
|
5297
|
-
if (
|
|
5298
|
-
|
|
5299
|
-
|
|
5664
|
+
if (_namespaceOf(el) === NS_HTML) return HTML_SCOPE.has(_tagNameOf(el));
|
|
5665
|
+
if (_namespaceOf(el) === NS_MATHML) {
|
|
5666
|
+
return MATHML_SPECIAL.has(_tagNameOf(el));
|
|
5667
|
+
}
|
|
5668
|
+
if (_namespaceOf(el) === NS_SVG) {
|
|
5669
|
+
return SVG_SPECIAL.has(_tagNameOf(el).toLowerCase());
|
|
5300
5670
|
}
|
|
5301
5671
|
return false;
|
|
5302
5672
|
};
|
|
@@ -5311,10 +5681,10 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5311
5681
|
/** @type {number} */ kind
|
|
5312
5682
|
) => {
|
|
5313
5683
|
if (isScopeBoundary(el)) return true;
|
|
5314
|
-
if (
|
|
5315
|
-
if (kind === SCOPE_BUTTON) return
|
|
5684
|
+
if (_namespaceOf(el) !== NS_HTML) return false;
|
|
5685
|
+
if (kind === SCOPE_BUTTON) return _tagNameOf(el) === "button";
|
|
5316
5686
|
if (kind === SCOPE_LIST_ITEM) {
|
|
5317
|
-
return
|
|
5687
|
+
return _tagNameOf(el) === "ol" || _tagNameOf(el) === "ul";
|
|
5318
5688
|
}
|
|
5319
5689
|
return false;
|
|
5320
5690
|
};
|
|
@@ -5326,7 +5696,9 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5326
5696
|
) => {
|
|
5327
5697
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
5328
5698
|
const el = open[i];
|
|
5329
|
-
if (
|
|
5699
|
+
if (_namespaceOf(el) === NS_HTML && _tagNameOf(el) === tagName) {
|
|
5700
|
+
return true;
|
|
5701
|
+
}
|
|
5330
5702
|
if (isBoundaryForKind(el, kind)) return false;
|
|
5331
5703
|
}
|
|
5332
5704
|
return false;
|
|
@@ -5350,9 +5722,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5350
5722
|
const set = typeof target === "string" ? null : target;
|
|
5351
5723
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
5352
5724
|
const el = open[i];
|
|
5353
|
-
if (
|
|
5354
|
-
if (set ? set.has(
|
|
5355
|
-
|
|
5725
|
+
if (_namespaceOf(el) === NS_HTML) {
|
|
5726
|
+
if (set ? set.has(_tagNameOf(el)) : _tagNameOf(el) === target) {
|
|
5727
|
+
return true;
|
|
5728
|
+
}
|
|
5729
|
+
if (TABLE_SCOPE_STOP.has(_tagNameOf(el))) return false;
|
|
5356
5730
|
}
|
|
5357
5731
|
}
|
|
5358
5732
|
return false;
|
|
@@ -5360,7 +5734,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5360
5734
|
const generateImpliedEndTags = (except = "") => {
|
|
5361
5735
|
while (open.length) {
|
|
5362
5736
|
const el = cur();
|
|
5363
|
-
if (
|
|
5737
|
+
if (
|
|
5738
|
+
_namespaceOf(el) === NS_HTML &&
|
|
5739
|
+
IMPLIED.has(_tagNameOf(el)) &&
|
|
5740
|
+
_tagNameOf(el) !== except
|
|
5741
|
+
) {
|
|
5364
5742
|
open.pop();
|
|
5365
5743
|
} else {
|
|
5366
5744
|
break;
|
|
@@ -5370,7 +5748,10 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5370
5748
|
const generateImpliedEndTagsThorough = () => {
|
|
5371
5749
|
while (open.length) {
|
|
5372
5750
|
const el = cur();
|
|
5373
|
-
if (
|
|
5751
|
+
if (
|
|
5752
|
+
_namespaceOf(el) === NS_HTML &&
|
|
5753
|
+
IMPLIED_THOROUGH.has(_tagNameOf(el))
|
|
5754
|
+
) {
|
|
5374
5755
|
open.pop();
|
|
5375
5756
|
} else {
|
|
5376
5757
|
break;
|
|
@@ -5384,7 +5765,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5384
5765
|
for (let i = afe.length - 1; i >= 0; i--) {
|
|
5385
5766
|
const e = afe[i];
|
|
5386
5767
|
if (e === AFE_MARKER) break;
|
|
5387
|
-
if (
|
|
5768
|
+
if (
|
|
5769
|
+
_tagNameOf(e) === _tagNameOf(el) &&
|
|
5770
|
+
_namespaceOf(e) === _namespaceOf(el) &&
|
|
5771
|
+
sameAttrs(e, el)
|
|
5772
|
+
) {
|
|
5388
5773
|
count++;
|
|
5389
5774
|
if (count === 3) {
|
|
5390
5775
|
afe.splice(i, 1);
|
|
@@ -5398,16 +5783,16 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5398
5783
|
/** @type {HtmlElement} */ a,
|
|
5399
5784
|
/** @type {HtmlElement} */ b
|
|
5400
5785
|
) => {
|
|
5401
|
-
const aStart =
|
|
5402
|
-
const aCount =
|
|
5403
|
-
const bStart =
|
|
5404
|
-
const bCount =
|
|
5786
|
+
const aStart = _nodeAttrStarts[a];
|
|
5787
|
+
const aCount = _nodeAttrCounts[a];
|
|
5788
|
+
const bStart = _nodeAttrStarts[b];
|
|
5789
|
+
const bCount = _nodeAttrCounts[b];
|
|
5405
5790
|
if (aCount !== bCount) return false;
|
|
5406
5791
|
// Names are unique (deduped), counts tiny — a nested scan beats a Map
|
|
5407
5792
|
// here on this formatting-element hot path.
|
|
5408
5793
|
for (let i = bStart; i < bStart + bCount; i++) {
|
|
5409
|
-
const j =
|
|
5410
|
-
if (j === 0 ||
|
|
5794
|
+
const j = _findAttr(aStart, aCount, _attrNames[i]);
|
|
5795
|
+
if (j === 0 || _attrValueOf(j) !== _attrValueOf(i)) return false;
|
|
5411
5796
|
}
|
|
5412
5797
|
return true;
|
|
5413
5798
|
};
|
|
@@ -5430,7 +5815,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5430
5815
|
}
|
|
5431
5816
|
for (; i < afe.length; i++) {
|
|
5432
5817
|
const e = afe[i];
|
|
5433
|
-
const el = mkEl(
|
|
5818
|
+
const el = mkEl(_tagNameOf(e), _namespaceOf(e), cloneAttrs(e), null);
|
|
5434
5819
|
const place = appropriatePlace();
|
|
5435
5820
|
insertAtPlace(place, el);
|
|
5436
5821
|
open.push(el);
|
|
@@ -5444,23 +5829,23 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5444
5829
|
// pop until a p has been popped
|
|
5445
5830
|
while (open.length) {
|
|
5446
5831
|
const el = /** @type {HtmlElement} */ (open.pop());
|
|
5447
|
-
|
|
5448
|
-
if (
|
|
5832
|
+
_nodeEnds[el] = tokenEnd;
|
|
5833
|
+
if (_namespaceOf(el) === NS_HTML && _tagNameOf(el) === "p") break;
|
|
5449
5834
|
}
|
|
5450
5835
|
};
|
|
5451
5836
|
|
|
5452
5837
|
const popUntil = (/** @type {string} */ tagName) => {
|
|
5453
5838
|
while (open.length) {
|
|
5454
5839
|
const el = /** @type {HtmlElement} */ (open.pop());
|
|
5455
|
-
|
|
5456
|
-
if (
|
|
5840
|
+
_nodeEnds[el] = tokenEnd;
|
|
5841
|
+
if (_namespaceOf(el) === NS_HTML && _tagNameOf(el) === tagName) break;
|
|
5457
5842
|
}
|
|
5458
5843
|
};
|
|
5459
5844
|
const popUntilOneOf = (/** @type {Set<string>} */ set) => {
|
|
5460
5845
|
while (open.length) {
|
|
5461
5846
|
const el = /** @type {HtmlElement} */ (open.pop());
|
|
5462
|
-
|
|
5463
|
-
if (
|
|
5847
|
+
_nodeEnds[el] = tokenEnd;
|
|
5848
|
+
if (_namespaceOf(el) === NS_HTML && set.has(_tagNameOf(el))) break;
|
|
5464
5849
|
}
|
|
5465
5850
|
};
|
|
5466
5851
|
|
|
@@ -5473,8 +5858,8 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5473
5858
|
last = true;
|
|
5474
5859
|
if (fragment) node = fragment;
|
|
5475
5860
|
}
|
|
5476
|
-
const tn =
|
|
5477
|
-
if (
|
|
5861
|
+
const tn = _tagNameOf(node);
|
|
5862
|
+
if (_namespaceOf(node) === NS_HTML) {
|
|
5478
5863
|
if ((tn === "td" || tn === "th") && !last) {
|
|
5479
5864
|
mode = MODE_IN_CELL;
|
|
5480
5865
|
return;
|
|
@@ -5567,7 +5952,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5567
5952
|
const useForeign =
|
|
5568
5953
|
open.length > 0 &&
|
|
5569
5954
|
ac &&
|
|
5570
|
-
|
|
5955
|
+
_namespaceOf(ac) !== NS_HTML &&
|
|
5571
5956
|
ty !== TOKEN_EOF &&
|
|
5572
5957
|
shouldUseForeignRules(ac, t);
|
|
5573
5958
|
if (useForeign) {
|
|
@@ -5581,7 +5966,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5581
5966
|
/** @type {HtmlElement} */ ac,
|
|
5582
5967
|
/** @type {Token} */ t
|
|
5583
5968
|
) => {
|
|
5584
|
-
if (
|
|
5969
|
+
if (_namespaceOf(ac) === NS_HTML) return false;
|
|
5585
5970
|
if (t.type === TOKEN_START_TAG) {
|
|
5586
5971
|
if (
|
|
5587
5972
|
mathmlTextIntegrationPoint(ac) &&
|
|
@@ -5591,8 +5976,8 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5591
5976
|
return false;
|
|
5592
5977
|
}
|
|
5593
5978
|
if (
|
|
5594
|
-
|
|
5595
|
-
|
|
5979
|
+
_namespaceOf(ac) === NS_MATHML &&
|
|
5980
|
+
_tagNameOf(ac) === "annotation-xml" &&
|
|
5596
5981
|
t.name === "svg"
|
|
5597
5982
|
) {
|
|
5598
5983
|
return false;
|
|
@@ -5611,19 +5996,27 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5611
5996
|
};
|
|
5612
5997
|
|
|
5613
5998
|
const mathmlTextIntegrationPoint = (/** @type {HtmlElement} */ el) =>
|
|
5614
|
-
|
|
5999
|
+
_namespaceOf(el) === NS_MATHML &&
|
|
6000
|
+
MATHML_TEXT_INTEGRATION.has(_tagNameOf(el));
|
|
5615
6001
|
const htmlIntegrationPoint = (/** @type {HtmlElement} */ el) => {
|
|
5616
|
-
if (
|
|
5617
|
-
const enc =
|
|
6002
|
+
if (_namespaceOf(el) === NS_MATHML && _tagNameOf(el) === "annotation-xml") {
|
|
6003
|
+
const enc = _findAttr(
|
|
6004
|
+
_nodeAttrStarts[el],
|
|
6005
|
+
_nodeAttrCounts[el],
|
|
6006
|
+
"encoding"
|
|
6007
|
+
);
|
|
5618
6008
|
if (enc !== 0) {
|
|
5619
|
-
const value =
|
|
6009
|
+
const value = _attrValueOf(enc).toLowerCase();
|
|
5620
6010
|
if (value === "text/html" || value === "application/xhtml+xml") {
|
|
5621
6011
|
return true;
|
|
5622
6012
|
}
|
|
5623
6013
|
}
|
|
5624
6014
|
return false;
|
|
5625
6015
|
}
|
|
5626
|
-
if (
|
|
6016
|
+
if (
|
|
6017
|
+
_namespaceOf(el) === NS_SVG &&
|
|
6018
|
+
SVG_SPECIAL.has(_tagNameOf(el).toLowerCase())
|
|
6019
|
+
) {
|
|
5627
6020
|
return true;
|
|
5628
6021
|
}
|
|
5629
6022
|
return false;
|
|
@@ -5633,23 +6026,23 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5633
6026
|
/** @type {Record<string, string>} */ (SVG_TAG_ADJUST)[name] || name;
|
|
5634
6027
|
// Adjust a foreign start tag's attribute run in place (the run is consumed
|
|
5635
6028
|
// only by this tag's element): SVG camelCase names are rewritten, and
|
|
5636
|
-
// namespaced names get the serializer-name flag (see `
|
|
6029
|
+
// namespaced names get the serializer-name flag (see `_attrSerializedName`).
|
|
5637
6030
|
const adjustForeignAttrs = (
|
|
5638
6031
|
/** @type {AttributeRun} */ run,
|
|
5639
6032
|
/** @type {number} */ ns
|
|
5640
6033
|
) => {
|
|
5641
6034
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
5642
|
-
const name =
|
|
6035
|
+
const name = _attrNames[i];
|
|
5643
6036
|
if (
|
|
5644
6037
|
ns === NS_SVG &&
|
|
5645
6038
|
/** @type {Record<string, string>} */ (SVG_ATTR_ADJUST)[name]
|
|
5646
6039
|
) {
|
|
5647
|
-
|
|
6040
|
+
_attrNames[i] = /** @type {Record<string, string>} */ (SVG_ATTR_ADJUST)[
|
|
5648
6041
|
name
|
|
5649
6042
|
];
|
|
5650
6043
|
}
|
|
5651
6044
|
if (/** @type {Record<string, string>} */ (FOREIGN_ATTR_NS)[name]) {
|
|
5652
|
-
|
|
6045
|
+
_attrFlags[i] |= 1;
|
|
5653
6046
|
}
|
|
5654
6047
|
}
|
|
5655
6048
|
return run;
|
|
@@ -5669,7 +6062,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5669
6062
|
}
|
|
5670
6063
|
if (t.type === TOKEN_DOCTYPE) return;
|
|
5671
6064
|
if (t.type === TOKEN_START_TAG) {
|
|
5672
|
-
const acn =
|
|
6065
|
+
const acn = _namespaceOf(adjustedCurrent());
|
|
5673
6066
|
if (
|
|
5674
6067
|
FOREIGN_BREAKOUT.has(t.name) ||
|
|
5675
6068
|
(t.name === "font" && hasFontBreakoutAttr(t.attrs))
|
|
@@ -5678,7 +6071,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5678
6071
|
while (open.length > 1) {
|
|
5679
6072
|
const c = cur();
|
|
5680
6073
|
if (
|
|
5681
|
-
|
|
6074
|
+
_namespaceOf(c) === NS_HTML ||
|
|
5682
6075
|
mathmlTextIntegrationPoint(c) ||
|
|
5683
6076
|
htmlIntegrationPoint(c)
|
|
5684
6077
|
) {
|
|
@@ -5708,8 +6101,8 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5708
6101
|
if (t.type === TOKEN_END_TAG) {
|
|
5709
6102
|
if (
|
|
5710
6103
|
t.name === "script" &&
|
|
5711
|
-
|
|
5712
|
-
|
|
6104
|
+
_tagNameOf(cur()) === "script" &&
|
|
6105
|
+
_namespaceOf(cur()) === NS_SVG
|
|
5713
6106
|
) {
|
|
5714
6107
|
open.pop();
|
|
5715
6108
|
return;
|
|
@@ -5719,7 +6112,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5719
6112
|
if (t.name === "p" || t.name === "br") {
|
|
5720
6113
|
while (
|
|
5721
6114
|
open.length > 1 &&
|
|
5722
|
-
|
|
6115
|
+
_namespaceOf(cur()) !== NS_HTML &&
|
|
5723
6116
|
!mathmlTextIntegrationPoint(cur()) &&
|
|
5724
6117
|
!htmlIntegrationPoint(cur())
|
|
5725
6118
|
) {
|
|
@@ -5731,18 +6124,21 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5731
6124
|
// any other end tag
|
|
5732
6125
|
let i = open.length - 1;
|
|
5733
6126
|
let node = open[i];
|
|
5734
|
-
if (
|
|
6127
|
+
if (_tagNameOf(node).toLowerCase() !== t.name) {
|
|
5735
6128
|
/* parse error */
|
|
5736
6129
|
}
|
|
5737
6130
|
while (i >= 0) {
|
|
5738
6131
|
node = open[i];
|
|
5739
6132
|
if (i === 0) return;
|
|
5740
|
-
if (
|
|
6133
|
+
if (
|
|
6134
|
+
_namespaceOf(node) !== NS_HTML &&
|
|
6135
|
+
_tagNameOf(node).toLowerCase() === t.name
|
|
6136
|
+
) {
|
|
5741
6137
|
while (open.length > i) open.pop();
|
|
5742
6138
|
return;
|
|
5743
6139
|
}
|
|
5744
6140
|
i--;
|
|
5745
|
-
if (open[i] &&
|
|
6141
|
+
if (open[i] && _namespaceOf(open[i]) === NS_HTML) {
|
|
5746
6142
|
runMode(t);
|
|
5747
6143
|
return;
|
|
5748
6144
|
}
|
|
@@ -5757,7 +6153,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5757
6153
|
) => {
|
|
5758
6154
|
// step 1
|
|
5759
6155
|
const c = cur();
|
|
5760
|
-
if (
|
|
6156
|
+
if (
|
|
6157
|
+
_namespaceOf(c) === NS_HTML &&
|
|
6158
|
+
_tagNameOf(c) === subject &&
|
|
6159
|
+
!afe.includes(c)
|
|
6160
|
+
) {
|
|
5761
6161
|
open.pop();
|
|
5762
6162
|
return true;
|
|
5763
6163
|
}
|
|
@@ -5768,7 +6168,10 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5768
6168
|
let fmtIdx = -1;
|
|
5769
6169
|
for (let i = afe.length - 1; i >= 0; i--) {
|
|
5770
6170
|
if (afe[i] === AFE_MARKER) break;
|
|
5771
|
-
if (
|
|
6171
|
+
if (
|
|
6172
|
+
_tagNameOf(afe[i]) === subject &&
|
|
6173
|
+
_namespaceOf(afe[i]) === NS_HTML
|
|
6174
|
+
) {
|
|
5772
6175
|
fmtIdx = i;
|
|
5773
6176
|
break;
|
|
5774
6177
|
}
|
|
@@ -5817,7 +6220,12 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5817
6220
|
continue;
|
|
5818
6221
|
}
|
|
5819
6222
|
// create clone
|
|
5820
|
-
const clone = mkEl(
|
|
6223
|
+
const clone = mkEl(
|
|
6224
|
+
_tagNameOf(node),
|
|
6225
|
+
_namespaceOf(node),
|
|
6226
|
+
cloneAttrs(node),
|
|
6227
|
+
null
|
|
6228
|
+
);
|
|
5821
6229
|
afe[nodeAfeIdx] = clone;
|
|
5822
6230
|
open[nodeIdx] = clone;
|
|
5823
6231
|
node = clone;
|
|
@@ -5832,16 +6240,21 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5832
6240
|
const place = placeForCommonAncestor(commonAncestor);
|
|
5833
6241
|
insertAtPlace(place, lastNode);
|
|
5834
6242
|
// create element for fmt token, take children of furthest
|
|
5835
|
-
const cloneFmt = mkEl(
|
|
6243
|
+
const cloneFmt = mkEl(
|
|
6244
|
+
_tagNameOf(fmt),
|
|
6245
|
+
_namespaceOf(fmt),
|
|
6246
|
+
cloneAttrs(fmt),
|
|
6247
|
+
null
|
|
6248
|
+
);
|
|
5836
6249
|
// Take all direct children of `furthest` (a template's content fragment
|
|
5837
6250
|
// deliberately stays put — mirrors childrenOf-less spec behavior here).
|
|
5838
|
-
let k =
|
|
5839
|
-
|
|
5840
|
-
|
|
6251
|
+
let k = _nodeFirstChildren[furthest];
|
|
6252
|
+
_nodeFirstChildren[furthest] = 0;
|
|
6253
|
+
_nodeLastChildren[furthest] = 0;
|
|
5841
6254
|
while (k !== 0) {
|
|
5842
|
-
const next =
|
|
5843
|
-
|
|
5844
|
-
|
|
6255
|
+
const next = _nodeNextSiblings[k];
|
|
6256
|
+
_nodeNextSiblings[k] = 0;
|
|
6257
|
+
_nodeParents[k] = 0;
|
|
5845
6258
|
appendTo(cloneFmt, k);
|
|
5846
6259
|
k = next;
|
|
5847
6260
|
}
|
|
@@ -5866,8 +6279,8 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5866
6279
|
/** @type {HtmlElement} */ commonAncestor
|
|
5867
6280
|
) => {
|
|
5868
6281
|
if (
|
|
5869
|
-
TABLE_CONTEXT.has(
|
|
5870
|
-
|
|
6282
|
+
TABLE_CONTEXT.has(_tagNameOf(commonAncestor)) &&
|
|
6283
|
+
_namespaceOf(commonAncestor) === NS_HTML
|
|
5871
6284
|
) {
|
|
5872
6285
|
// foster
|
|
5873
6286
|
// reuse appropriatePlace logic but rooted differently: emulate
|
|
@@ -5875,15 +6288,15 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5875
6288
|
let lastTable = -1;
|
|
5876
6289
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
5877
6290
|
if (
|
|
5878
|
-
|
|
5879
|
-
|
|
6291
|
+
_tagNameOf(open[i]) === "template" &&
|
|
6292
|
+
_namespaceOf(open[i]) === NS_HTML &&
|
|
5880
6293
|
lastTemplate === -1
|
|
5881
6294
|
) {
|
|
5882
6295
|
lastTemplate = i;
|
|
5883
6296
|
}
|
|
5884
6297
|
if (
|
|
5885
|
-
|
|
5886
|
-
|
|
6298
|
+
_tagNameOf(open[i]) === "table" &&
|
|
6299
|
+
_namespaceOf(open[i]) === NS_HTML &&
|
|
5887
6300
|
lastTable === -1
|
|
5888
6301
|
) {
|
|
5889
6302
|
lastTable = i;
|
|
@@ -5897,7 +6310,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5897
6310
|
}
|
|
5898
6311
|
if (lastTable === -1) return { parent: open[0], beforeNode: 0 };
|
|
5899
6312
|
const table = open[lastTable];
|
|
5900
|
-
const tp =
|
|
6313
|
+
const tp = _nodeParents[table];
|
|
5901
6314
|
if (tp !== 0) return { parent: tp, beforeNode: table };
|
|
5902
6315
|
return { parent: open[lastTable - 1], beforeNode: 0 };
|
|
5903
6316
|
}
|
|
@@ -5905,20 +6318,20 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
5905
6318
|
};
|
|
5906
6319
|
|
|
5907
6320
|
const detach = (/** @type {HtmlNodeRef} */ node) => {
|
|
5908
|
-
const p =
|
|
6321
|
+
const p = _nodeParents[node];
|
|
5909
6322
|
if (p === 0) return;
|
|
5910
6323
|
let prev = 0;
|
|
5911
|
-
let c =
|
|
6324
|
+
let c = _nodeFirstChildren[p];
|
|
5912
6325
|
while (c !== 0 && c !== node) {
|
|
5913
6326
|
prev = c;
|
|
5914
|
-
c =
|
|
6327
|
+
c = _nodeNextSiblings[c];
|
|
5915
6328
|
}
|
|
5916
6329
|
if (c === 0) return;
|
|
5917
|
-
if (prev === 0)
|
|
5918
|
-
else
|
|
5919
|
-
if (
|
|
5920
|
-
|
|
5921
|
-
|
|
6330
|
+
if (prev === 0) _nodeFirstChildren[p] = _nodeNextSiblings[node];
|
|
6331
|
+
else _nodeNextSiblings[prev] = _nodeNextSiblings[node];
|
|
6332
|
+
if (_nodeLastChildren[p] === node) _nodeLastChildren[p] = prev;
|
|
6333
|
+
_nodeNextSiblings[node] = 0;
|
|
6334
|
+
_nodeParents[node] = 0;
|
|
5922
6335
|
};
|
|
5923
6336
|
|
|
5924
6337
|
// ---------- insertion modes ----------
|
|
@@ -6082,13 +6495,13 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6082
6495
|
if (t.type === TOKEN_DOCTYPE) {
|
|
6083
6496
|
// `skip.doctype` drops the node only; quirks detection below is unaffected.
|
|
6084
6497
|
if (!skipDoctype) {
|
|
6085
|
-
const dt =
|
|
6086
|
-
|
|
6498
|
+
const dt = _allocNode(NodeType.Doctype, t.start, t.end);
|
|
6499
|
+
_nodeStrings[dt] = t.name;
|
|
6087
6500
|
// At most one doctype node is ever inserted (later doctype tokens
|
|
6088
6501
|
// are ignored), so its ids live in two per-parse scalars.
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6502
|
+
_doctypePublicId = t.publicId;
|
|
6503
|
+
_doctypeSystemId = t.systemId;
|
|
6504
|
+
_appendChild(doc, dt);
|
|
6092
6505
|
}
|
|
6093
6506
|
quirks = isQuirky(t.name, t.publicId, t.systemId);
|
|
6094
6507
|
mode = MODE_BEFORE_HTML;
|
|
@@ -6112,7 +6525,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6112
6525
|
}
|
|
6113
6526
|
if (t.type === TOKEN_START_TAG && t.name === "html") {
|
|
6114
6527
|
const el = mkEl("html", NS_HTML, t.attrs, t.pos);
|
|
6115
|
-
|
|
6528
|
+
_appendChild(doc, el);
|
|
6116
6529
|
open.push(el);
|
|
6117
6530
|
mode = MODE_BEFORE_HEAD;
|
|
6118
6531
|
return;
|
|
@@ -6121,7 +6534,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6121
6534
|
return;
|
|
6122
6535
|
}
|
|
6123
6536
|
const el = mkEl("html", NS_HTML, EMPTY_ATTRS, null);
|
|
6124
|
-
|
|
6537
|
+
_appendChild(doc, el);
|
|
6125
6538
|
open.push(el);
|
|
6126
6539
|
mode = MODE_BEFORE_HEAD;
|
|
6127
6540
|
process(t);
|
|
@@ -6197,10 +6610,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6197
6610
|
mode = MODE_IN_TEMPLATE;
|
|
6198
6611
|
templateModes.push(MODE_IN_TEMPLATE);
|
|
6199
6612
|
const el = cur();
|
|
6200
|
-
const fragment =
|
|
6613
|
+
const fragment = _allocNode(NodeType.DocumentFragment, 0, 0);
|
|
6614
|
+
_nodeStrings[fragment] = "";
|
|
6201
6615
|
// Parent link so the iterative walk can ascend out of the content.
|
|
6202
|
-
|
|
6203
|
-
|
|
6616
|
+
_nodeParents[fragment] = el;
|
|
6617
|
+
_nodeTemplateContents[el] = fragment;
|
|
6204
6618
|
return;
|
|
6205
6619
|
}
|
|
6206
6620
|
if (t.name === "head") return;
|
|
@@ -6343,10 +6757,10 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6343
6757
|
const anyOtherEndTag = (/** @type {string} */ name) => {
|
|
6344
6758
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6345
6759
|
const node = open[i];
|
|
6346
|
-
if (
|
|
6760
|
+
if (_namespaceOf(node) === NS_HTML && _tagNameOf(node) === name) {
|
|
6347
6761
|
generateImpliedEndTags(name);
|
|
6348
6762
|
while (open.length > i) {
|
|
6349
|
-
|
|
6763
|
+
_nodeEnds[open[open.length - 1]] = tokenEnd;
|
|
6350
6764
|
open.pop();
|
|
6351
6765
|
}
|
|
6352
6766
|
return;
|
|
@@ -6369,7 +6783,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6369
6783
|
}
|
|
6370
6784
|
if (name === "body") {
|
|
6371
6785
|
const second = open[1];
|
|
6372
|
-
if (
|
|
6786
|
+
if (
|
|
6787
|
+
!second ||
|
|
6788
|
+
_tagNameOf(second) !== "body" ||
|
|
6789
|
+
open.some(isHtmlTemplateEl)
|
|
6790
|
+
) {
|
|
6373
6791
|
return;
|
|
6374
6792
|
}
|
|
6375
6793
|
framesetOk = false;
|
|
@@ -6378,7 +6796,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6378
6796
|
}
|
|
6379
6797
|
if (name === "frameset") {
|
|
6380
6798
|
const second = open[1];
|
|
6381
|
-
if (!second ||
|
|
6799
|
+
if (!second || _tagNameOf(second) !== "body") return;
|
|
6382
6800
|
if (!framesetOk) return;
|
|
6383
6801
|
detach(second);
|
|
6384
6802
|
while (open.length > 1) open.pop();
|
|
@@ -6393,7 +6811,9 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6393
6811
|
}
|
|
6394
6812
|
if (HEADING.has(name)) {
|
|
6395
6813
|
closeIfPInButtonScope();
|
|
6396
|
-
if (
|
|
6814
|
+
if (_namespaceOf(cur()) === NS_HTML && HEADING.has(_tagNameOf(cur()))) {
|
|
6815
|
+
open.pop();
|
|
6816
|
+
}
|
|
6397
6817
|
insertHtmlElement(name, t.attrs, t.pos);
|
|
6398
6818
|
return;
|
|
6399
6819
|
}
|
|
@@ -6419,14 +6839,17 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6419
6839
|
framesetOk = false;
|
|
6420
6840
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6421
6841
|
const node = open[i];
|
|
6422
|
-
if (
|
|
6842
|
+
if (_namespaceOf(node) === NS_HTML && _tagNameOf(node) === "li") {
|
|
6423
6843
|
generateImpliedEndTags("li");
|
|
6424
6844
|
popUntil("li");
|
|
6425
6845
|
break;
|
|
6426
6846
|
}
|
|
6427
6847
|
if (
|
|
6428
6848
|
isSpecial(node) &&
|
|
6429
|
-
!(
|
|
6849
|
+
!(
|
|
6850
|
+
_namespaceOf(node) === NS_HTML &&
|
|
6851
|
+
ADDRESS_DIV_P.has(_tagNameOf(node))
|
|
6852
|
+
)
|
|
6430
6853
|
) {
|
|
6431
6854
|
break;
|
|
6432
6855
|
}
|
|
@@ -6440,16 +6863,19 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6440
6863
|
for (let i = open.length - 1; i >= 0; i--) {
|
|
6441
6864
|
const node = open[i];
|
|
6442
6865
|
if (
|
|
6443
|
-
|
|
6444
|
-
(
|
|
6866
|
+
_namespaceOf(node) === NS_HTML &&
|
|
6867
|
+
(_tagNameOf(node) === "dd" || _tagNameOf(node) === "dt")
|
|
6445
6868
|
) {
|
|
6446
|
-
generateImpliedEndTags(
|
|
6447
|
-
popUntil(
|
|
6869
|
+
generateImpliedEndTags(_tagNameOf(node));
|
|
6870
|
+
popUntil(_tagNameOf(node));
|
|
6448
6871
|
break;
|
|
6449
6872
|
}
|
|
6450
6873
|
if (
|
|
6451
6874
|
isSpecial(node) &&
|
|
6452
|
-
!(
|
|
6875
|
+
!(
|
|
6876
|
+
_namespaceOf(node) === NS_HTML &&
|
|
6877
|
+
ADDRESS_DIV_P.has(_tagNameOf(node))
|
|
6878
|
+
)
|
|
6453
6879
|
) {
|
|
6454
6880
|
break;
|
|
6455
6881
|
}
|
|
@@ -6477,9 +6903,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6477
6903
|
// if there's an <a> in afe after last marker
|
|
6478
6904
|
for (let i = afe.length - 1; i >= 0; i--) {
|
|
6479
6905
|
if (afe[i] === AFE_MARKER) break;
|
|
6480
|
-
if (
|
|
6906
|
+
if (_tagNameOf(afe[i]) === "a") {
|
|
6481
6907
|
adoptionAgency("a", t.pos);
|
|
6482
|
-
const idx = afe.findIndex(
|
|
6908
|
+
const idx = afe.findIndex(
|
|
6909
|
+
(e) => e !== AFE_MARKER && _tagNameOf(e) === "a"
|
|
6910
|
+
);
|
|
6483
6911
|
if (idx !== -1) {
|
|
6484
6912
|
const el = afe[idx];
|
|
6485
6913
|
afe.splice(idx, 1);
|
|
@@ -6541,16 +6969,16 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6541
6969
|
resetInsertionMode();
|
|
6542
6970
|
} else if (
|
|
6543
6971
|
fragment &&
|
|
6544
|
-
|
|
6545
|
-
|
|
6972
|
+
_namespaceOf(fragment) === NS_HTML &&
|
|
6973
|
+
_tagNameOf(fragment) === "select"
|
|
6546
6974
|
) {
|
|
6547
6975
|
return;
|
|
6548
6976
|
}
|
|
6549
6977
|
reconstructAfe();
|
|
6550
6978
|
insertHtmlElement("input", t.attrs, t.pos);
|
|
6551
6979
|
open.pop();
|
|
6552
|
-
const ty =
|
|
6553
|
-
if (ty === 0 ||
|
|
6980
|
+
const ty = _findAttr(t.attrs.start, t.attrs.count, "type");
|
|
6981
|
+
if (ty === 0 || _attrValueOf(ty).toLowerCase() !== "hidden") {
|
|
6554
6982
|
framesetOk = false;
|
|
6555
6983
|
}
|
|
6556
6984
|
return;
|
|
@@ -6561,8 +6989,10 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6561
6989
|
return;
|
|
6562
6990
|
}
|
|
6563
6991
|
if (name === "hr") {
|
|
6564
|
-
if (
|
|
6565
|
-
|
|
6992
|
+
if (_namespaceOf(cur()) === NS_HTML && _tagNameOf(cur()) === "option") {
|
|
6993
|
+
open.pop();
|
|
6994
|
+
}
|
|
6995
|
+
if (_namespaceOf(cur()) === NS_HTML && _tagNameOf(cur()) === "optgroup") {
|
|
6566
6996
|
open.pop();
|
|
6567
6997
|
}
|
|
6568
6998
|
closeIfPInButtonScope();
|
|
@@ -6611,11 +7041,13 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6611
7041
|
return;
|
|
6612
7042
|
}
|
|
6613
7043
|
if (name === "optgroup" || name === "option") {
|
|
6614
|
-
if (
|
|
7044
|
+
if (_namespaceOf(cur()) === NS_HTML && _tagNameOf(cur()) === "option") {
|
|
7045
|
+
open.pop();
|
|
7046
|
+
}
|
|
6615
7047
|
if (
|
|
6616
7048
|
name === "optgroup" &&
|
|
6617
|
-
|
|
6618
|
-
|
|
7049
|
+
_namespaceOf(cur()) === NS_HTML &&
|
|
7050
|
+
_tagNameOf(cur()) === "optgroup"
|
|
6619
7051
|
) {
|
|
6620
7052
|
open.pop();
|
|
6621
7053
|
}
|
|
@@ -6659,7 +7091,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6659
7091
|
// this tag's element); the camelCase name serializes as itself.
|
|
6660
7092
|
const adjustMathmlAttrs = (/** @type {AttributeRun} */ run) => {
|
|
6661
7093
|
for (let i = run.start; i < run.start + run.count; i++) {
|
|
6662
|
-
if (
|
|
7094
|
+
if (_attrNames[i] === "definitionurl") _attrNames[i] = "definitionURL";
|
|
6663
7095
|
}
|
|
6664
7096
|
return run;
|
|
6665
7097
|
};
|
|
@@ -6785,7 +7217,10 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6785
7217
|
// current element: a non-matching name (e.g. a fragment context), or
|
|
6786
7218
|
// a name that ran straight to EOF with no delimiter (`</script` at
|
|
6787
7219
|
// EOF — the tokenizer still emits a partial tag there).
|
|
6788
|
-
if (
|
|
7220
|
+
if (
|
|
7221
|
+
cur() &&
|
|
7222
|
+
(t.name !== _tagNameOf(cur()) || t.pos.end === t.pos.nameEnd)
|
|
7223
|
+
) {
|
|
6789
7224
|
insertCharacters(
|
|
6790
7225
|
source.slice(t.pos.start, t.pos.end),
|
|
6791
7226
|
t.pos.start,
|
|
@@ -6793,7 +7228,8 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6793
7228
|
);
|
|
6794
7229
|
return;
|
|
6795
7230
|
}
|
|
6796
|
-
|
|
7231
|
+
// span the close tag, like every other explicit-close pop
|
|
7232
|
+
_nodeEnds[/** @type {HtmlElement} */ (open.pop())] = tokenEnd;
|
|
6797
7233
|
mode = originalMode;
|
|
6798
7234
|
}
|
|
6799
7235
|
};
|
|
@@ -6805,7 +7241,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6805
7241
|
modes.inTable = (t) => {
|
|
6806
7242
|
if (t.type === TOKEN_CHAR) {
|
|
6807
7243
|
const c = cur();
|
|
6808
|
-
if (TABLE_CONTEXT.has(
|
|
7244
|
+
if (TABLE_CONTEXT.has(_tagNameOf(c)) && _namespaceOf(c) === NS_HTML) {
|
|
6809
7245
|
pendingTableChars = { list: [], hasNonWs: false };
|
|
6810
7246
|
originalMode = mode;
|
|
6811
7247
|
mode = MODE_IN_TABLE_TEXT;
|
|
@@ -6860,8 +7296,8 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6860
7296
|
return modes.inHead(t);
|
|
6861
7297
|
}
|
|
6862
7298
|
if (name === "input") {
|
|
6863
|
-
const ty =
|
|
6864
|
-
if (ty !== 0 &&
|
|
7299
|
+
const ty = _findAttr(t.attrs.start, t.attrs.count, "type");
|
|
7300
|
+
if (ty !== 0 && _attrValueOf(ty).toLowerCase() === "hidden") {
|
|
6865
7301
|
insertHtmlElement("input", t.attrs, t.pos);
|
|
6866
7302
|
open.pop();
|
|
6867
7303
|
return;
|
|
@@ -6931,7 +7367,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6931
7367
|
const clearStackToTableContext = () => {
|
|
6932
7368
|
while (open.length) {
|
|
6933
7369
|
const c = cur();
|
|
6934
|
-
if (
|
|
7370
|
+
if (_namespaceOf(c) === NS_HTML && CLEAR_TABLE.has(_tagNameOf(c))) {
|
|
6935
7371
|
break;
|
|
6936
7372
|
}
|
|
6937
7373
|
open.pop();
|
|
@@ -6940,7 +7376,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6940
7376
|
const clearStackToTableBodyContext = () => {
|
|
6941
7377
|
while (open.length) {
|
|
6942
7378
|
const c = cur();
|
|
6943
|
-
if (
|
|
7379
|
+
if (_namespaceOf(c) === NS_HTML && CLEAR_TABLE_BODY.has(_tagNameOf(c))) {
|
|
6944
7380
|
break;
|
|
6945
7381
|
}
|
|
6946
7382
|
open.pop();
|
|
@@ -6949,7 +7385,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6949
7385
|
const clearStackToTableRowContext = () => {
|
|
6950
7386
|
while (open.length) {
|
|
6951
7387
|
const c = cur();
|
|
6952
|
-
if (
|
|
7388
|
+
if (_namespaceOf(c) === NS_HTML && CLEAR_TABLE_ROW.has(_tagNameOf(c))) {
|
|
6953
7389
|
break;
|
|
6954
7390
|
}
|
|
6955
7391
|
open.pop();
|
|
@@ -6982,7 +7418,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
6982
7418
|
if (t.type === TOKEN_CHAR) {
|
|
6983
7419
|
const r = leadingWs(t, true);
|
|
6984
7420
|
if (!r) return;
|
|
6985
|
-
if (
|
|
7421
|
+
if (_tagNameOf(cur()) !== "colgroup") return;
|
|
6986
7422
|
open.pop();
|
|
6987
7423
|
mode = MODE_IN_TABLE;
|
|
6988
7424
|
process(r);
|
|
@@ -7000,7 +7436,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7000
7436
|
return;
|
|
7001
7437
|
}
|
|
7002
7438
|
if (t.type === TOKEN_END_TAG && t.name === "colgroup") {
|
|
7003
|
-
if (
|
|
7439
|
+
if (_tagNameOf(cur()) !== "colgroup") return;
|
|
7004
7440
|
open.pop();
|
|
7005
7441
|
mode = MODE_IN_TABLE;
|
|
7006
7442
|
return;
|
|
@@ -7013,7 +7449,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7013
7449
|
return modes.inHead(t);
|
|
7014
7450
|
}
|
|
7015
7451
|
if (t.type === TOKEN_EOF) return modes.inBody(t);
|
|
7016
|
-
if (
|
|
7452
|
+
if (_tagNameOf(cur()) !== "colgroup") return;
|
|
7017
7453
|
open.pop();
|
|
7018
7454
|
mode = MODE_IN_TABLE;
|
|
7019
7455
|
process(t);
|
|
@@ -7196,9 +7632,11 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7196
7632
|
return;
|
|
7197
7633
|
}
|
|
7198
7634
|
if (t.type === TOKEN_END_TAG && t.name === "frameset") {
|
|
7199
|
-
if (
|
|
7635
|
+
if (_tagNameOf(cur()) === "html") return;
|
|
7200
7636
|
open.pop();
|
|
7201
|
-
if (!fragment &&
|
|
7637
|
+
if (!fragment && _tagNameOf(cur()) !== "frameset") {
|
|
7638
|
+
mode = MODE_AFTER_FRAMESET;
|
|
7639
|
+
}
|
|
7202
7640
|
return;
|
|
7203
7641
|
}
|
|
7204
7642
|
if (t.type === TOKEN_START_TAG && t.name === "frame") {
|
|
@@ -7271,7 +7709,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7271
7709
|
}
|
|
7272
7710
|
fragment = mkEl(ctxName, ctxNs, EMPTY_ATTRS, null);
|
|
7273
7711
|
const htmlEl = mkEl("html", NS_HTML, EMPTY_ATTRS, null);
|
|
7274
|
-
|
|
7712
|
+
_appendChild(doc, htmlEl);
|
|
7275
7713
|
open.push(htmlEl);
|
|
7276
7714
|
if (ctxNs !== NS_HTML) {
|
|
7277
7715
|
mode = MODE_IN_BODY;
|
|
@@ -7297,7 +7735,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7297
7735
|
}
|
|
7298
7736
|
|
|
7299
7737
|
// ---------- tokenizer callbacks ----------
|
|
7300
|
-
const decode =
|
|
7738
|
+
const decode = decodeEntities;
|
|
7301
7739
|
|
|
7302
7740
|
let swallowNextNewline = false;
|
|
7303
7741
|
// Set by the tokenizer when it hits EOF mid-tag; such a partial tag is
|
|
@@ -7326,12 +7764,12 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7326
7764
|
const dispatch = () =>
|
|
7327
7765
|
process(/** @type {Token} */ (/** @type {unknown} */ (tok)));
|
|
7328
7766
|
|
|
7329
|
-
|
|
7767
|
+
tokenize(source, pos, {
|
|
7330
7768
|
isForeign: () => {
|
|
7331
7769
|
const ac = adjustedCurrent();
|
|
7332
|
-
return open.length > 0 && ac !== 0 &&
|
|
7770
|
+
return open.length > 0 && ac !== 0 && _namespaceOf(ac) !== NS_HTML;
|
|
7333
7771
|
},
|
|
7334
|
-
fragmentContext: fragment ?
|
|
7772
|
+
fragmentContext: fragment ? _tagNameOf(fragment) : undefined,
|
|
7335
7773
|
parseError: (input, code) => {
|
|
7336
7774
|
if (code === "eof-in-tag") eofInTag = true;
|
|
7337
7775
|
},
|
|
@@ -7353,7 +7791,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7353
7791
|
// The tokenizer emits CDATA sections through this callback too.
|
|
7354
7792
|
if (input.startsWith("<![CDATA[", start)) {
|
|
7355
7793
|
const ac = adjustedCurrent();
|
|
7356
|
-
if (open.length > 0 && ac &&
|
|
7794
|
+
if (open.length > 0 && ac && _namespaceOf(ac) !== NS_HTML) {
|
|
7357
7795
|
const innerEnd = input.endsWith("]]>", end) ? end - 3 : end;
|
|
7358
7796
|
const data = input.slice(start + 9, innerEnd).replace(/\r\n?/g, "\n");
|
|
7359
7797
|
if (data !== "") {
|
|
@@ -7424,11 +7862,13 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7424
7862
|
const top = adjustedCurrent();
|
|
7425
7863
|
const rawMode =
|
|
7426
7864
|
mode === MODE_TEXT ||
|
|
7427
|
-
(top &&
|
|
7865
|
+
(top &&
|
|
7866
|
+
_namespaceOf(top) === NS_HTML &&
|
|
7867
|
+
_tagNameOf(top) === "plaintext");
|
|
7428
7868
|
const noDecode =
|
|
7429
7869
|
top &&
|
|
7430
|
-
|
|
7431
|
-
NO_DECODE_TEXT.has(
|
|
7870
|
+
_namespaceOf(top) === NS_HTML &&
|
|
7871
|
+
NO_DECODE_TEXT.has(_tagNameOf(top)) &&
|
|
7432
7872
|
(mode === MODE_TEXT || mode === MODE_IN_BODY);
|
|
7433
7873
|
let data = noDecode ? s : decode(s, false);
|
|
7434
7874
|
// In RAWTEXT/RCDATA/script/PLAINTEXT, NULL becomes U+FFFD (tokenizer
|
|
@@ -7449,12 +7889,12 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7449
7889
|
},
|
|
7450
7890
|
attribute: (input, nameStart, nameEnd, valueStart, valueEnd, quoteType) => {
|
|
7451
7891
|
const name = internLowerName(ATTR_NAME_INTERN, input, nameStart, nameEnd);
|
|
7452
|
-
if (pendAttrStart === 0) pendAttrStart =
|
|
7892
|
+
if (pendAttrStart === 0) pendAttrStart = _attrCount + 1;
|
|
7453
7893
|
// Drop duplicate attribute names (per spec). Plain loop avoids a
|
|
7454
7894
|
// per-attribute closure allocation on this hot path.
|
|
7455
7895
|
let dup = false;
|
|
7456
|
-
for (let i = pendAttrStart; i <=
|
|
7457
|
-
if (
|
|
7896
|
+
for (let i = pendAttrStart; i <= _attrCount; i++) {
|
|
7897
|
+
if (_attrNames[i] === name) {
|
|
7458
7898
|
dup = true;
|
|
7459
7899
|
break;
|
|
7460
7900
|
}
|
|
@@ -7464,7 +7904,7 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7464
7904
|
// demand: consumers re-resolve requests from it and the offsets must
|
|
7465
7905
|
// stay aligned with the source. Only a valueless attribute stores an
|
|
7466
7906
|
// override ("").
|
|
7467
|
-
|
|
7907
|
+
_allocAttr(
|
|
7468
7908
|
name,
|
|
7469
7909
|
valueStart !== -1 ? null : "",
|
|
7470
7910
|
nameStart,
|
|
@@ -7491,7 +7931,8 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7491
7931
|
// The reused token carries the tag's attribute run; every consumer of
|
|
7492
7932
|
// `t.attrs` runs synchronously within this dispatch.
|
|
7493
7933
|
tok.attrs.start = pendAttrStart;
|
|
7494
|
-
tok.attrs.count =
|
|
7934
|
+
tok.attrs.count =
|
|
7935
|
+
pendAttrStart === 0 ? 0 : _attrCount + 1 - pendAttrStart;
|
|
7495
7936
|
pendAttrStart = 0;
|
|
7496
7937
|
tok.selfClosing = selfClosing;
|
|
7497
7938
|
tok.swallowNewline = false;
|
|
@@ -7532,30 +7973,31 @@ const buildHtmlAst = (source, fragmentContext, skip = EMPTY_SKIP) => {
|
|
|
7532
7973
|
* @returns {HtmlNodeRef} clone
|
|
7533
7974
|
*/
|
|
7534
7975
|
const cloneSubtree = (node) => {
|
|
7535
|
-
const ty =
|
|
7536
|
-
const clone =
|
|
7537
|
-
|
|
7976
|
+
const ty = _nodeTypes[node];
|
|
7977
|
+
const clone = _allocNode(ty, _nodeStarts[node], _nodeEnds[node]);
|
|
7978
|
+
_nodeStrings[clone] = _nodeStrings[node];
|
|
7538
7979
|
if (ty !== NodeType.Element) return clone;
|
|
7539
|
-
|
|
7980
|
+
_nodeFlags[clone] = _nodeFlags[node];
|
|
7540
7981
|
const attrs = cloneAttrs(node);
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7982
|
+
_nodeAttrStarts[clone] = attrs.start;
|
|
7983
|
+
_nodeAttrCounts[clone] = attrs.count;
|
|
7984
|
+
_nodeTagEnds[clone] = _nodeTagEnds[node];
|
|
7985
|
+
_nodeNameEnds[clone] = _nodeNameEnds[node];
|
|
7545
7986
|
// Empty body span: the clone re-emits no raw-text dependency.
|
|
7546
|
-
|
|
7547
|
-
for (let k =
|
|
7548
|
-
|
|
7987
|
+
_nodeContentEnds[clone] = _nodeTagEnds[node];
|
|
7988
|
+
for (let k = _nodeFirstChildren[node]; k !== 0; k = _nodeNextSiblings[k]) {
|
|
7989
|
+
_appendChild(clone, cloneSubtree(k));
|
|
7549
7990
|
}
|
|
7550
7991
|
// Clone `<template>` content into the clone's own fragment.
|
|
7551
|
-
const tc =
|
|
7992
|
+
const tc = _nodeTemplateContents[node];
|
|
7552
7993
|
if (tc !== 0) {
|
|
7553
|
-
const fragment =
|
|
7994
|
+
const fragment = _allocNode(NodeType.DocumentFragment, 0, 0);
|
|
7995
|
+
_nodeStrings[fragment] = "";
|
|
7554
7996
|
// Parent link so the iterative walk can ascend out of the content.
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
for (let k =
|
|
7558
|
-
|
|
7997
|
+
_nodeParents[fragment] = clone;
|
|
7998
|
+
_nodeTemplateContents[clone] = fragment;
|
|
7999
|
+
for (let k = _nodeFirstChildren[tc]; k !== 0; k = _nodeNextSiblings[k]) {
|
|
8000
|
+
_appendChild(fragment, cloneSubtree(k));
|
|
7559
8001
|
}
|
|
7560
8002
|
}
|
|
7561
8003
|
return clone;
|
|
@@ -7572,17 +8014,19 @@ const selectedOption = (select) => {
|
|
|
7572
8014
|
const options = [];
|
|
7573
8015
|
/** @param {HtmlElement} el element */
|
|
7574
8016
|
const collect = (el) => {
|
|
7575
|
-
for (let c =
|
|
7576
|
-
if (
|
|
7577
|
-
|
|
7578
|
-
|
|
8017
|
+
for (let c = _nodeFirstChildren[el]; c !== 0; c = _nodeNextSiblings[c]) {
|
|
8018
|
+
if (_nodeTypes[c] !== NodeType.Element || _namespaceOf(c) !== NS_HTML) {
|
|
8019
|
+
continue;
|
|
8020
|
+
}
|
|
8021
|
+
if (_nodeStrings[c] === "option") options.push(c);
|
|
8022
|
+
else if (_nodeStrings[c] === "optgroup") collect(c);
|
|
7579
8023
|
}
|
|
7580
8024
|
};
|
|
7581
8025
|
collect(select);
|
|
7582
8026
|
if (options.length === 0) return 0;
|
|
7583
8027
|
for (let i = options.length - 1; i >= 0; i--) {
|
|
7584
8028
|
const el = options[i];
|
|
7585
|
-
if (
|
|
8029
|
+
if (_findAttr(_nodeAttrStarts[el], _nodeAttrCounts[el], "selected") !== 0) {
|
|
7586
8030
|
return el;
|
|
7587
8031
|
}
|
|
7588
8032
|
}
|
|
@@ -7597,24 +8041,32 @@ const selectedOption = (select) => {
|
|
|
7597
8041
|
*/
|
|
7598
8042
|
const mirrorSelectedContent = (node, select) => {
|
|
7599
8043
|
// A `<template>`'s children live in its content fragment.
|
|
7600
|
-
const tc =
|
|
8044
|
+
const tc = _nodeTemplateContents[node];
|
|
7601
8045
|
const container = tc !== 0 ? tc : node;
|
|
7602
|
-
for (
|
|
7603
|
-
|
|
7604
|
-
|
|
8046
|
+
for (
|
|
8047
|
+
let child = _nodeFirstChildren[container];
|
|
8048
|
+
child !== 0;
|
|
8049
|
+
child = _nodeNextSiblings[child]
|
|
8050
|
+
) {
|
|
8051
|
+
if (_nodeTypes[child] !== NodeType.Element) continue;
|
|
8052
|
+
if (_namespaceOf(child) === NS_HTML && _nodeStrings[child] === "select") {
|
|
7605
8053
|
mirrorSelectedContent(child, child);
|
|
7606
8054
|
} else if (
|
|
7607
8055
|
select !== 0 &&
|
|
7608
|
-
|
|
7609
|
-
|
|
8056
|
+
_namespaceOf(child) === NS_HTML &&
|
|
8057
|
+
_nodeStrings[child] === "selectedcontent"
|
|
7610
8058
|
) {
|
|
7611
8059
|
const option = selectedOption(select);
|
|
7612
8060
|
if (option !== 0) {
|
|
7613
8061
|
// Replace the children with clones of the option's subtree.
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
for (
|
|
7617
|
-
|
|
8062
|
+
_nodeFirstChildren[child] = 0;
|
|
8063
|
+
_nodeLastChildren[child] = 0;
|
|
8064
|
+
for (
|
|
8065
|
+
let k = _nodeFirstChildren[option];
|
|
8066
|
+
k !== 0;
|
|
8067
|
+
k = _nodeNextSiblings[k]
|
|
8068
|
+
) {
|
|
8069
|
+
_appendChild(child, cloneSubtree(k));
|
|
7618
8070
|
}
|
|
7619
8071
|
}
|
|
7620
8072
|
} else {
|
|
@@ -7661,7 +8113,7 @@ const parseDoctype = (/** @type {string} */ raw) => {
|
|
|
7661
8113
|
|
|
7662
8114
|
/**
|
|
7663
8115
|
* @typedef {object} HtmlProcessOptions
|
|
7664
|
-
* @property {string=} fragmentContext context element tag name for fragment parsing (see `
|
|
8116
|
+
* @property {string=} fragmentContext context element tag name for fragment parsing (see `parseHtml`); the HTML analog of the CSS parser's `as` parse-mode option
|
|
7665
8117
|
* @property {HtmlAstSkip=} skip node kinds to omit from the AST for speed/memory (see `HtmlAstSkip`)
|
|
7666
8118
|
*/
|
|
7667
8119
|
|
|
@@ -7674,26 +8126,26 @@ const parseDoctype = (/** @type {string} */ raw) => {
|
|
|
7674
8126
|
* @param {HtmlProcessOptions} options process options
|
|
7675
8127
|
*/
|
|
7676
8128
|
const grammar = (input, visitors, options) => {
|
|
7677
|
-
const root =
|
|
8129
|
+
const root = parseHtml(input, 0, options);
|
|
7678
8130
|
|
|
7679
8131
|
// Iterative depth-first walk over the link columns: `firstChild`
|
|
7680
8132
|
// / `nextSibling` descend, the `parent` column ascends, so arbitrarily deep
|
|
7681
8133
|
// markup can't overflow the call stack (the old recursive walk died at
|
|
7682
8134
|
// ~10⁵ nesting). A `<template>`'s content fragment is visited before the
|
|
7683
8135
|
// element's children; ascending out of it continues with those children
|
|
7684
|
-
// (the fragment is never in a sibling chain, `
|
|
8136
|
+
// (the fragment is never in a sibling chain, `_nodeNextSiblings` = 0).
|
|
7685
8137
|
/**
|
|
7686
8138
|
* Fire a node's `enter` visitors; true when the walk may descend.
|
|
7687
8139
|
* @param {HtmlNodeRef} node node
|
|
7688
8140
|
* @returns {boolean} false when a visitor called `skipChildren()`
|
|
7689
8141
|
*/
|
|
7690
8142
|
const enter = (node) => {
|
|
7691
|
-
const b = visitors[
|
|
8143
|
+
const b = visitors[_nodeTypes[node]];
|
|
7692
8144
|
if (b === undefined || b.enter.length === 0) return true;
|
|
7693
8145
|
_walkSkip = false;
|
|
7694
|
-
|
|
7695
|
-
const p =
|
|
7696
|
-
|
|
8146
|
+
_currentNode = node;
|
|
8147
|
+
const p = _nodeParents[node];
|
|
8148
|
+
_currentParent = p === 0 ? null : p;
|
|
7697
8149
|
const e = b.enter;
|
|
7698
8150
|
for (let i = 0; i < e.length; i++) e[i](A);
|
|
7699
8151
|
const skip = _walkSkip;
|
|
@@ -7705,11 +8157,11 @@ const grammar = (input, visitors, options) => {
|
|
|
7705
8157
|
* @param {HtmlNodeRef} node node
|
|
7706
8158
|
*/
|
|
7707
8159
|
const exit = (node) => {
|
|
7708
|
-
const b = visitors[
|
|
8160
|
+
const b = visitors[_nodeTypes[node]];
|
|
7709
8161
|
if (b === undefined) return;
|
|
7710
|
-
|
|
7711
|
-
const p =
|
|
7712
|
-
|
|
8162
|
+
_currentNode = node;
|
|
8163
|
+
const p = _nodeParents[node];
|
|
8164
|
+
_currentParent = p === 0 ? null : p;
|
|
7713
8165
|
const x = b.exit;
|
|
7714
8166
|
for (let i = 0; i < x.length; i++) x[i](A);
|
|
7715
8167
|
};
|
|
@@ -7719,13 +8171,13 @@ const grammar = (input, visitors, options) => {
|
|
|
7719
8171
|
* @returns {HtmlNodeRef} first inner node (0 = leaf)
|
|
7720
8172
|
*/
|
|
7721
8173
|
const firstInner = (node) => {
|
|
7722
|
-
const ty =
|
|
8174
|
+
const ty = _nodeTypes[node];
|
|
7723
8175
|
if (ty === NodeType.Element) {
|
|
7724
|
-
const tc =
|
|
7725
|
-
return tc !== 0 ? tc :
|
|
8176
|
+
const tc = _nodeTemplateContents[node];
|
|
8177
|
+
return tc !== 0 ? tc : _nodeFirstChildren[node];
|
|
7726
8178
|
}
|
|
7727
8179
|
if (ty === NodeType.Document || ty === NodeType.DocumentFragment) {
|
|
7728
|
-
return
|
|
8180
|
+
return _nodeFirstChildren[node];
|
|
7729
8181
|
}
|
|
7730
8182
|
return 0;
|
|
7731
8183
|
};
|
|
@@ -7743,16 +8195,16 @@ const grammar = (input, visitors, options) => {
|
|
|
7743
8195
|
for (;;) {
|
|
7744
8196
|
exit(node);
|
|
7745
8197
|
if (node === root) break descend;
|
|
7746
|
-
const parent =
|
|
8198
|
+
const parent = _nodeParents[node];
|
|
7747
8199
|
// Out of a template's content fragment: the element's children follow.
|
|
7748
|
-
if (
|
|
7749
|
-
const first =
|
|
8200
|
+
if (_nodeTemplateContents[parent] === node) {
|
|
8201
|
+
const first = _nodeFirstChildren[parent];
|
|
7750
8202
|
if (first !== 0) {
|
|
7751
8203
|
node = first;
|
|
7752
8204
|
continue descend;
|
|
7753
8205
|
}
|
|
7754
8206
|
} else {
|
|
7755
|
-
const sibling =
|
|
8207
|
+
const sibling = _nodeNextSiblings[node];
|
|
7756
8208
|
if (sibling !== 0) {
|
|
7757
8209
|
node = sibling;
|
|
7758
8210
|
continue descend;
|
|
@@ -7764,7 +8216,7 @@ const grammar = (input, visitors, options) => {
|
|
|
7764
8216
|
|
|
7765
8217
|
// The walk consumed the tree: release the side arrays' heap references so
|
|
7766
8218
|
// the reused columns don't pin this parse's strings until the next parse.
|
|
7767
|
-
|
|
8219
|
+
_releaseAstColumns();
|
|
7768
8220
|
};
|
|
7769
8221
|
|
|
7770
8222
|
/**
|
|
@@ -7772,14 +8224,16 @@ const grammar = (input, visitors, options) => {
|
|
|
7772
8224
|
* `grammar`. Babel-style usage:
|
|
7773
8225
|
*
|
|
7774
8226
|
* ```
|
|
7775
|
-
*
|
|
7776
|
-
* processor.process(source);
|
|
8227
|
+
* new SourceProcessor({ skip }).use({ [NodeType.Element]: (path) => {}, [NodeType.Comment]: { enter, exit } }).process(source);
|
|
7777
8228
|
* ```
|
|
7778
8229
|
* @extends {GenericSourceProcessor<HtmlPath, HtmlProcessOptions>}
|
|
7779
8230
|
*/
|
|
7780
8231
|
class SourceProcessor extends GenericSourceProcessor {
|
|
7781
|
-
|
|
7782
|
-
|
|
8232
|
+
/**
|
|
8233
|
+
* @param {HtmlProcessOptions=} options default process options (`skip`, `fragmentContext`) merged under each `process` call's own options
|
|
8234
|
+
*/
|
|
8235
|
+
constructor(options) {
|
|
8236
|
+
super(grammar, options);
|
|
7783
8237
|
}
|
|
7784
8238
|
}
|
|
7785
8239
|
|
|
@@ -7974,7 +8428,7 @@ const parseSrcset = (input) => {
|
|
|
7974
8428
|
/**
|
|
7975
8429
|
* @returns {void}
|
|
7976
8430
|
*/
|
|
7977
|
-
function
|
|
8431
|
+
function tokenizeDescriptor() {
|
|
7978
8432
|
// 8.1. Descriptor tokenizer: Skip whitespace
|
|
7979
8433
|
collectCharacters(LEADING_SPACES_REGEXP);
|
|
7980
8434
|
|
|
@@ -8128,22 +8582,162 @@ const parseSrcset = (input) => {
|
|
|
8128
8582
|
}
|
|
8129
8583
|
// Otherwise, follow these sub steps:
|
|
8130
8584
|
else {
|
|
8131
|
-
|
|
8585
|
+
tokenizeDescriptor();
|
|
8132
8586
|
}
|
|
8133
8587
|
|
|
8134
8588
|
// 16. Return to the step labeled splitting loop.
|
|
8135
8589
|
}
|
|
8136
8590
|
};
|
|
8137
8591
|
|
|
8592
|
+
// The spec's "URL-potentially-surrounded-by-spaces" cleanup: C0 controls,
|
|
8593
|
+
// DEL..C1 and U+00A0 are stripped from the value after the whitespace trim.
|
|
8594
|
+
// eslint-disable-next-line no-control-regex
|
|
8595
|
+
const IGNORE_CHARS_REGEXP = /[\u0000-\u001F\u007F-\u009F\u00A0]/g;
|
|
8596
|
+
|
|
8597
|
+
/**
|
|
8598
|
+
* Parse a `src`-like URL attribute value: trim ASCII whitespace / U+00A0 from
|
|
8599
|
+
* both ends (offsets preserved for source rewriting), then strip ignorable
|
|
8600
|
+
* control characters. Throws when nothing remains.
|
|
8601
|
+
* @param {string} input attribute value
|
|
8602
|
+
* @returns {ParsedSource[]} parsed src
|
|
8603
|
+
*/
|
|
8604
|
+
const parseSrc = (input) => {
|
|
8605
|
+
const len = input.length;
|
|
8606
|
+
if (len === 0) throw new Error("Must be non-empty");
|
|
8607
|
+
|
|
8608
|
+
let start = 0;
|
|
8609
|
+
let end = len;
|
|
8610
|
+
|
|
8611
|
+
while (start < end) {
|
|
8612
|
+
const code = input.charCodeAt(start);
|
|
8613
|
+
if (code > 32 && code !== 160) break;
|
|
8614
|
+
start++;
|
|
8615
|
+
}
|
|
8616
|
+
|
|
8617
|
+
if (start === end) throw new Error("Must be non-empty");
|
|
8618
|
+
|
|
8619
|
+
while (end > start) {
|
|
8620
|
+
const code = input.charCodeAt(end - 1);
|
|
8621
|
+
if (code > 32 && code !== 160) break;
|
|
8622
|
+
end--;
|
|
8623
|
+
}
|
|
8624
|
+
|
|
8625
|
+
let value = input.slice(start, end);
|
|
8626
|
+
|
|
8627
|
+
if (IGNORE_CHARS_REGEXP.test(value)) {
|
|
8628
|
+
value = value.replace(IGNORE_CHARS_REGEXP, "");
|
|
8629
|
+
if (value.length === 0) throw new Error("Must be non-empty");
|
|
8630
|
+
}
|
|
8631
|
+
|
|
8632
|
+
return [[value, start, end]];
|
|
8633
|
+
};
|
|
8634
|
+
|
|
8635
|
+
/**
|
|
8636
|
+
* Extracts the `icon-uri` value of an `msapplication-task` meta content
|
|
8637
|
+
* (`name=…;action-uri=…;icon-uri=…`) — the other parts are page URLs, not assets.
|
|
8638
|
+
* @param {string} input input
|
|
8639
|
+
* @returns {ParsedSource[]} parsed icon-uri
|
|
8640
|
+
*/
|
|
8641
|
+
const parseMsapplicationTask = (input) => {
|
|
8642
|
+
const len = input.length;
|
|
8643
|
+
let pos = 0;
|
|
8644
|
+
while (pos < len) {
|
|
8645
|
+
let sep = input.indexOf(";", pos);
|
|
8646
|
+
if (sep === -1) sep = len;
|
|
8647
|
+
const eq = input.indexOf("=", pos);
|
|
8648
|
+
if (eq !== -1 && eq < sep) {
|
|
8649
|
+
const key = input.slice(pos, eq).trim().toLowerCase();
|
|
8650
|
+
if (key === "icon-uri") {
|
|
8651
|
+
let start = eq + 1;
|
|
8652
|
+
let end = sep;
|
|
8653
|
+
while (start < end && isSpace(input.charCodeAt(start))) {
|
|
8654
|
+
start++;
|
|
8655
|
+
}
|
|
8656
|
+
while (end > start && isSpace(input.charCodeAt(end - 1))) {
|
|
8657
|
+
end--;
|
|
8658
|
+
}
|
|
8659
|
+
if (start === end) return [];
|
|
8660
|
+
return [[input.slice(start, end), start, end]];
|
|
8661
|
+
}
|
|
8662
|
+
}
|
|
8663
|
+
pos = sep + 1;
|
|
8664
|
+
}
|
|
8665
|
+
return [];
|
|
8666
|
+
};
|
|
8667
|
+
|
|
8668
|
+
// CSS syntax is loaded on first `parseCssUrls` call, not at module load —
|
|
8669
|
+
// `HtmlGenerator` deliberately defers it the same way, and most documents
|
|
8670
|
+
// never carry a URL-bearing SVG presentation attribute.
|
|
8671
|
+
/** @type {typeof import("../css/syntax") | undefined} */
|
|
8672
|
+
let _cssSyntax;
|
|
8673
|
+
|
|
8674
|
+
/**
|
|
8675
|
+
* Extracts `url(...)` references from a CSS value (an SVG presentation
|
|
8676
|
+
* attribute). Reuses webpack's CSS lexer so quoting/escaping match the CSS
|
|
8677
|
+
* spec; returns the `parseSrc` shape so the shared emit path maps and rewrites
|
|
8678
|
+
* the spans. Unquoted `url(path)` rewrites the content span; quoted
|
|
8679
|
+
* `url("path")` rewrites the inner string span (quotes preserved).
|
|
8680
|
+
* @param {string} input attribute value (a CSS component-value list)
|
|
8681
|
+
* @returns {ParsedSource[]} url references
|
|
8682
|
+
*/
|
|
8683
|
+
const parseCssUrls = (input) => {
|
|
8684
|
+
const {
|
|
8685
|
+
TT_EOF,
|
|
8686
|
+
TT_FUNCTION,
|
|
8687
|
+
TT_STRING,
|
|
8688
|
+
TT_URL,
|
|
8689
|
+
TT_WHITESPACE,
|
|
8690
|
+
TokenStream,
|
|
8691
|
+
equalsLowerCase
|
|
8692
|
+
} = _cssSyntax || (_cssSyntax = require("../css/syntax"));
|
|
8693
|
+
const ts = new TokenStream(input);
|
|
8694
|
+
/** @type {ParsedSource[]} */
|
|
8695
|
+
const result = [];
|
|
8696
|
+
for (;;) {
|
|
8697
|
+
const t = ts.consume();
|
|
8698
|
+
if (t.type === TT_EOF) break;
|
|
8699
|
+
if (t.type === TT_URL) {
|
|
8700
|
+
if (t.contentEnd > t.contentStart) {
|
|
8701
|
+
result.push([
|
|
8702
|
+
input.slice(t.contentStart, t.contentEnd),
|
|
8703
|
+
t.contentStart,
|
|
8704
|
+
t.contentEnd
|
|
8705
|
+
]);
|
|
8706
|
+
}
|
|
8707
|
+
} else if (
|
|
8708
|
+
t.type === TT_FUNCTION &&
|
|
8709
|
+
equalsLowerCase(input.slice(t.start, t.end - 1), "url")
|
|
8710
|
+
) {
|
|
8711
|
+
let s = ts.consume();
|
|
8712
|
+
while (s.type === TT_WHITESPACE) s = ts.consume();
|
|
8713
|
+
if (s.type === TT_STRING) {
|
|
8714
|
+
const quote = input.charCodeAt(s.start);
|
|
8715
|
+
const innerStart = s.start + 1;
|
|
8716
|
+
// Drop the closing quote, unless the string is unterminated at EOF.
|
|
8717
|
+
const innerEnd =
|
|
8718
|
+
input.charCodeAt(s.end - 1) === quote ? s.end - 1 : s.end;
|
|
8719
|
+
if (innerEnd > innerStart) {
|
|
8720
|
+
result.push([
|
|
8721
|
+
input.slice(innerStart, innerEnd),
|
|
8722
|
+
innerStart,
|
|
8723
|
+
innerEnd
|
|
8724
|
+
]);
|
|
8725
|
+
}
|
|
8726
|
+
}
|
|
8727
|
+
}
|
|
8728
|
+
}
|
|
8729
|
+
return result;
|
|
8730
|
+
};
|
|
8731
|
+
|
|
8138
8732
|
// Babel's `path.skip()`, children-only: set by `A.skipChildren()` during an
|
|
8139
8733
|
// `enter` dispatch, consumed by the walk.
|
|
8140
8734
|
let _walkSkip = false;
|
|
8141
8735
|
// The walk's current position (`A.node` / `A.parent` read these; module-level
|
|
8142
8736
|
// so the accessor methods' defaults avoid self-referential `this` typing).
|
|
8143
8737
|
/** @type {HtmlNodeRef} */
|
|
8144
|
-
let
|
|
8738
|
+
let _currentNode = 0;
|
|
8145
8739
|
/** @type {HtmlNodeRef | null} */
|
|
8146
|
-
let
|
|
8740
|
+
let _currentParent = null;
|
|
8147
8741
|
|
|
8148
8742
|
/* eslint-disable jsdoc/require-template -- `A` below is the accessor const, not a type parameter */
|
|
8149
8743
|
/**
|
|
@@ -8156,20 +8750,20 @@ let _curParent = null;
|
|
|
8156
8750
|
// AST field-access seam (mirrors the CSS parser's `A`): every AST field a
|
|
8157
8751
|
// consumer reads goes through one of these accessors, so the node
|
|
8158
8752
|
// representation can change underneath without touching consumers. `n` is an
|
|
8159
|
-
// `HtmlNodeRef`; results are valid until the next `
|
|
8753
|
+
// `HtmlNodeRef`; results are valid until the next `parseHtml` call.
|
|
8160
8754
|
const A = {
|
|
8161
8755
|
// === path position (rebound by the walk before every visitor call) ===
|
|
8162
8756
|
/**
|
|
8163
8757
|
* @returns {HtmlNodeRef} current node — only valid during a visitor callback
|
|
8164
8758
|
*/
|
|
8165
8759
|
get node() {
|
|
8166
|
-
return
|
|
8760
|
+
return _currentNode;
|
|
8167
8761
|
},
|
|
8168
8762
|
/**
|
|
8169
8763
|
* @returns {HtmlNodeRef | null} enclosing node (null = the document root)
|
|
8170
8764
|
*/
|
|
8171
8765
|
get parent() {
|
|
8172
|
-
return
|
|
8766
|
+
return _currentParent;
|
|
8173
8767
|
},
|
|
8174
8768
|
/** Stop the walk descending into the current node (enter only). */
|
|
8175
8769
|
skipChildren() {
|
|
@@ -8180,43 +8774,43 @@ const A = {
|
|
|
8180
8774
|
* @param {HtmlNodeRef=} n node
|
|
8181
8775
|
* @returns {number} `NodeType`
|
|
8182
8776
|
*/
|
|
8183
|
-
type(n =
|
|
8184
|
-
return
|
|
8777
|
+
type(n = _currentNode) {
|
|
8778
|
+
return _nodeTypes[n];
|
|
8185
8779
|
},
|
|
8186
8780
|
/**
|
|
8187
8781
|
* @param {HtmlNodeRef=} n node
|
|
8188
8782
|
* @returns {number} start offset
|
|
8189
8783
|
*/
|
|
8190
|
-
start(n =
|
|
8191
|
-
return
|
|
8784
|
+
start(n = _currentNode) {
|
|
8785
|
+
return _nodeStarts[n];
|
|
8192
8786
|
},
|
|
8193
8787
|
/**
|
|
8194
8788
|
* @param {HtmlNodeRef=} n node
|
|
8195
8789
|
* @returns {number} end offset
|
|
8196
8790
|
*/
|
|
8197
|
-
end(n =
|
|
8198
|
-
return
|
|
8791
|
+
end(n = _currentNode) {
|
|
8792
|
+
return _nodeEnds[n];
|
|
8199
8793
|
},
|
|
8200
8794
|
/**
|
|
8201
8795
|
* @param {HtmlElement=} n element
|
|
8202
8796
|
* @returns {string} lowercased (foreign-content: adjusted) tag name
|
|
8203
8797
|
*/
|
|
8204
|
-
tagName(n =
|
|
8205
|
-
return
|
|
8798
|
+
tagName(n = _currentNode) {
|
|
8799
|
+
return _nodeStrings[n];
|
|
8206
8800
|
},
|
|
8207
8801
|
/**
|
|
8208
8802
|
* @param {HtmlElement=} n element
|
|
8209
8803
|
* @returns {number} `NS_*` namespace
|
|
8210
8804
|
*/
|
|
8211
|
-
namespace(n =
|
|
8212
|
-
return
|
|
8805
|
+
namespace(n = _currentNode) {
|
|
8806
|
+
return _nodeFlags[n] & NS_MASK;
|
|
8213
8807
|
},
|
|
8214
8808
|
/**
|
|
8215
8809
|
* @param {HtmlElement=} n element
|
|
8216
8810
|
* @returns {boolean} true for void elements
|
|
8217
8811
|
*/
|
|
8218
|
-
selfClosing(n =
|
|
8219
|
-
return (
|
|
8812
|
+
selfClosing(n = _currentNode) {
|
|
8813
|
+
return (_nodeFlags[n] & FLAG_SELF_CLOSING) !== 0;
|
|
8220
8814
|
},
|
|
8221
8815
|
// materialized attribute list — test/tooling convenience, allocates; the
|
|
8222
8816
|
// parser reads attributes through the scalar accessors below
|
|
@@ -8224,18 +8818,18 @@ const A = {
|
|
|
8224
8818
|
* @param {HtmlElement=} n element
|
|
8225
8819
|
* @returns {HtmlAttribute[]} materialized attributes
|
|
8226
8820
|
*/
|
|
8227
|
-
attributes(n =
|
|
8821
|
+
attributes(n = _currentNode) {
|
|
8228
8822
|
const out = [];
|
|
8229
|
-
const start =
|
|
8230
|
-
for (let i = start; i < start +
|
|
8823
|
+
const start = _nodeAttrStarts[n];
|
|
8824
|
+
for (let i = start; i < start + _nodeAttrCounts[n]; i++) {
|
|
8231
8825
|
out.push({
|
|
8232
|
-
name:
|
|
8233
|
-
value:
|
|
8234
|
-
serializedName:
|
|
8235
|
-
nameStart:
|
|
8236
|
-
nameEnd:
|
|
8237
|
-
valueStart:
|
|
8238
|
-
valueEnd:
|
|
8826
|
+
name: _attrNames[i],
|
|
8827
|
+
value: _attrValueOf(i),
|
|
8828
|
+
serializedName: _attrSerializedName(i),
|
|
8829
|
+
nameStart: _attrNameStarts[i],
|
|
8830
|
+
nameEnd: _attrNameEnds[i],
|
|
8831
|
+
valueStart: _attrValueStarts[i],
|
|
8832
|
+
valueEnd: _attrValueEnds[i]
|
|
8239
8833
|
});
|
|
8240
8834
|
}
|
|
8241
8835
|
return out;
|
|
@@ -8244,8 +8838,8 @@ const A = {
|
|
|
8244
8838
|
* @param {HtmlElement=} n element
|
|
8245
8839
|
* @returns {number} attribute count
|
|
8246
8840
|
*/
|
|
8247
|
-
attributeCount(n =
|
|
8248
|
-
return
|
|
8841
|
+
attributeCount(n = _currentNode) {
|
|
8842
|
+
return _nodeAttrCounts[n];
|
|
8249
8843
|
},
|
|
8250
8844
|
/**
|
|
8251
8845
|
* The i-th attribute of an element, as an id for the `attribute*` reads.
|
|
@@ -8253,8 +8847,8 @@ const A = {
|
|
|
8253
8847
|
* @param {HtmlElement=} n element
|
|
8254
8848
|
* @returns {HtmlAttributeRef} attribute ref
|
|
8255
8849
|
*/
|
|
8256
|
-
attributeAt(i, n =
|
|
8257
|
-
return
|
|
8850
|
+
attributeAt(i, n = _currentNode) {
|
|
8851
|
+
return _nodeAttrStarts[n] + i;
|
|
8258
8852
|
},
|
|
8259
8853
|
/**
|
|
8260
8854
|
* Linear lookup by (lowercased) name.
|
|
@@ -8262,92 +8856,92 @@ const A = {
|
|
|
8262
8856
|
* @param {HtmlElement=} n element
|
|
8263
8857
|
* @returns {HtmlAttributeRef} attribute ref (0 = not present)
|
|
8264
8858
|
*/
|
|
8265
|
-
findAttribute(name, n =
|
|
8266
|
-
return
|
|
8859
|
+
findAttribute(name, n = _currentNode) {
|
|
8860
|
+
return _findAttr(_nodeAttrStarts[n], _nodeAttrCounts[n], name);
|
|
8267
8861
|
},
|
|
8268
8862
|
/**
|
|
8269
8863
|
* @param {HtmlAttributeRef} a attribute ref
|
|
8270
8864
|
* @returns {string} lowercased (foreign-content: adjusted) attribute name
|
|
8271
8865
|
*/
|
|
8272
8866
|
attributeName(a) {
|
|
8273
|
-
return
|
|
8867
|
+
return _attrNames[a];
|
|
8274
8868
|
},
|
|
8275
8869
|
/**
|
|
8276
8870
|
* @param {HtmlAttributeRef} a attribute ref
|
|
8277
8871
|
* @returns {string} raw (undecoded) attribute value ("" when valueless)
|
|
8278
8872
|
*/
|
|
8279
8873
|
attributeValue(a) {
|
|
8280
|
-
return
|
|
8874
|
+
return _attrValueOf(a);
|
|
8281
8875
|
},
|
|
8282
8876
|
/**
|
|
8283
8877
|
* @param {HtmlAttributeRef} a attribute ref
|
|
8284
8878
|
* @returns {number} name start offset
|
|
8285
8879
|
*/
|
|
8286
8880
|
attributeNameStart(a) {
|
|
8287
|
-
return
|
|
8881
|
+
return _attrNameStarts[a];
|
|
8288
8882
|
},
|
|
8289
8883
|
/**
|
|
8290
8884
|
* @param {HtmlAttributeRef} a attribute ref
|
|
8291
8885
|
* @returns {number} name end offset
|
|
8292
8886
|
*/
|
|
8293
8887
|
attributeNameEnd(a) {
|
|
8294
|
-
return
|
|
8888
|
+
return _attrNameEnds[a];
|
|
8295
8889
|
},
|
|
8296
8890
|
/**
|
|
8297
8891
|
* @param {HtmlAttributeRef} a attribute ref
|
|
8298
8892
|
* @returns {number} value start offset (-1 when valueless or on adoption-agency clones)
|
|
8299
8893
|
*/
|
|
8300
8894
|
attributeValueStart(a) {
|
|
8301
|
-
return
|
|
8895
|
+
return _attrValueStarts[a];
|
|
8302
8896
|
},
|
|
8303
8897
|
/**
|
|
8304
8898
|
* @param {HtmlAttributeRef} a attribute ref
|
|
8305
8899
|
* @returns {number} value end offset
|
|
8306
8900
|
*/
|
|
8307
8901
|
attributeValueEnd(a) {
|
|
8308
|
-
return
|
|
8902
|
+
return _attrValueEnds[a];
|
|
8309
8903
|
},
|
|
8310
8904
|
/**
|
|
8311
8905
|
* @param {HtmlElement=} n element
|
|
8312
8906
|
* @returns {number} end offset of the opening tag (after `>`)
|
|
8313
8907
|
*/
|
|
8314
|
-
tagEnd(n =
|
|
8315
|
-
return
|
|
8908
|
+
tagEnd(n = _currentNode) {
|
|
8909
|
+
return _nodeTagEnds[n];
|
|
8316
8910
|
},
|
|
8317
8911
|
/**
|
|
8318
8912
|
* @param {HtmlElement=} n element
|
|
8319
8913
|
* @returns {number} end offset of the tag name
|
|
8320
8914
|
*/
|
|
8321
|
-
nameEnd(n =
|
|
8322
|
-
return
|
|
8915
|
+
nameEnd(n = _currentNode) {
|
|
8916
|
+
return _nodeNameEnds[n];
|
|
8323
8917
|
},
|
|
8324
8918
|
/**
|
|
8325
8919
|
* @param {HtmlElement=} n element
|
|
8326
8920
|
* @returns {number} under `skip.text`, end offset of a raw-text element's body (`tagEnd` when empty)
|
|
8327
8921
|
*/
|
|
8328
|
-
contentEnd(n =
|
|
8329
|
-
return
|
|
8922
|
+
contentEnd(n = _currentNode) {
|
|
8923
|
+
return _nodeContentEnds[n];
|
|
8330
8924
|
},
|
|
8331
8925
|
/**
|
|
8332
8926
|
* @param {HtmlElement=} n element
|
|
8333
8927
|
* @returns {HtmlDocumentFragment} `<template>` content fragment (0 = none)
|
|
8334
8928
|
*/
|
|
8335
|
-
templateContent(n =
|
|
8336
|
-
return
|
|
8929
|
+
templateContent(n = _currentNode) {
|
|
8930
|
+
return _nodeTemplateContents[n];
|
|
8337
8931
|
},
|
|
8338
8932
|
/**
|
|
8339
8933
|
* @param {HtmlText | HtmlComment=} n text / comment node
|
|
8340
8934
|
* @returns {string} decoded text / comment data
|
|
8341
8935
|
*/
|
|
8342
|
-
data(n =
|
|
8343
|
-
return
|
|
8936
|
+
data(n = _currentNode) {
|
|
8937
|
+
return _nodeStrings[n];
|
|
8344
8938
|
},
|
|
8345
8939
|
/**
|
|
8346
8940
|
* @param {HtmlDoctype=} n doctype node
|
|
8347
8941
|
* @returns {string} doctype name
|
|
8348
8942
|
*/
|
|
8349
|
-
doctypeName(n =
|
|
8350
|
-
return
|
|
8943
|
+
doctypeName(n = _currentNode) {
|
|
8944
|
+
return _nodeStrings[n];
|
|
8351
8945
|
},
|
|
8352
8946
|
// The doctype ids are per-parse scalars (a document has at most one
|
|
8353
8947
|
// doctype node); the node parameter is accepted for call-shape uniformity.
|
|
@@ -8356,44 +8950,46 @@ const A = {
|
|
|
8356
8950
|
* @returns {string | null} doctype public id
|
|
8357
8951
|
*/
|
|
8358
8952
|
doctypePublicId(_n) {
|
|
8359
|
-
return
|
|
8953
|
+
return _doctypePublicId;
|
|
8360
8954
|
},
|
|
8361
8955
|
/**
|
|
8362
8956
|
* @param {HtmlDoctype=} _n doctype node
|
|
8363
8957
|
* @returns {string | null} doctype system id
|
|
8364
8958
|
*/
|
|
8365
8959
|
doctypeSystemId(_n) {
|
|
8366
|
-
return
|
|
8960
|
+
return _doctypeSystemId;
|
|
8367
8961
|
},
|
|
8368
8962
|
// === tree links (0 = none) ===
|
|
8369
8963
|
/**
|
|
8370
8964
|
* @param {HtmlNodeRef=} n node
|
|
8371
8965
|
* @returns {HtmlNodeRef} first child
|
|
8372
8966
|
*/
|
|
8373
|
-
firstChild(n =
|
|
8374
|
-
return
|
|
8967
|
+
firstChild(n = _currentNode) {
|
|
8968
|
+
return _nodeFirstChildren[n];
|
|
8375
8969
|
},
|
|
8376
8970
|
/**
|
|
8377
8971
|
* @param {HtmlNodeRef=} n node
|
|
8378
8972
|
* @returns {HtmlNodeRef} next sibling
|
|
8379
8973
|
*/
|
|
8380
|
-
nextSibling(n =
|
|
8381
|
-
return
|
|
8974
|
+
nextSibling(n = _currentNode) {
|
|
8975
|
+
return _nodeNextSiblings[n];
|
|
8382
8976
|
},
|
|
8383
8977
|
/**
|
|
8384
8978
|
* @param {HtmlNodeRef=} n node
|
|
8385
8979
|
* @returns {HtmlNodeRef} parent node (a `<template>`'s content links to its fragment)
|
|
8386
8980
|
*/
|
|
8387
|
-
parentOf(n =
|
|
8388
|
-
return
|
|
8981
|
+
parentOf(n = _currentNode) {
|
|
8982
|
+
return _nodeParents[n];
|
|
8389
8983
|
},
|
|
8390
8984
|
/**
|
|
8391
8985
|
* @param {HtmlNodeRef=} n node
|
|
8392
8986
|
* @returns {HtmlNodeRef[]} materialized child list — test/tooling convenience, allocates
|
|
8393
8987
|
*/
|
|
8394
|
-
children(n =
|
|
8988
|
+
children(n = _currentNode) {
|
|
8395
8989
|
const out = [];
|
|
8396
|
-
for (let c =
|
|
8990
|
+
for (let c = _nodeFirstChildren[n]; c !== 0; c = _nodeNextSiblings[c]) {
|
|
8991
|
+
out.push(c);
|
|
8992
|
+
}
|
|
8397
8993
|
return out;
|
|
8398
8994
|
}
|
|
8399
8995
|
};
|
|
@@ -8406,12 +9002,19 @@ module.exports.NodeType = NodeType;
|
|
|
8406
9002
|
module.exports.QUOTE_DOUBLE = QUOTE_DOUBLE;
|
|
8407
9003
|
module.exports.QUOTE_NONE = QUOTE_NONE;
|
|
8408
9004
|
module.exports.QUOTE_SINGLE = QUOTE_SINGLE;
|
|
8409
|
-
module.exports.SVG_TAG_ADJUST = SVG_TAG_ADJUST;
|
|
8410
|
-
module.exports.SourceProcessor = SourceProcessor;
|
|
8411
9005
|
// Exposed so HtmlParser can map user-configured (lowercased) tag names to
|
|
8412
9006
|
// the adjusted camelCase names the AST carries for foreign content.
|
|
8413
|
-
module.exports.
|
|
8414
|
-
module.exports.
|
|
8415
|
-
module.exports.
|
|
9007
|
+
module.exports.SVG_TAG_ADJUST = SVG_TAG_ADJUST;
|
|
9008
|
+
module.exports.SourceProcessor = SourceProcessor;
|
|
9009
|
+
module.exports.decodeEntities = decodeEntities;
|
|
9010
|
+
module.exports.escapeAttribute = escapeAttribute;
|
|
9011
|
+
module.exports.escapeText = escapeText;
|
|
9012
|
+
// WHATWG "ASCII whitespace" (tab / LF / FF / CR / space) — the tokenizer's
|
|
9013
|
+
// whitespace class, exported under the spec's name.
|
|
9014
|
+
module.exports.isAsciiWhitespace = isSpace;
|
|
9015
|
+
module.exports.parseCssUrls = parseCssUrls;
|
|
9016
|
+
module.exports.parseHtml = parseHtml;
|
|
9017
|
+
module.exports.parseMsapplicationTask = parseMsapplicationTask;
|
|
9018
|
+
module.exports.parseSrc = parseSrc;
|
|
8416
9019
|
module.exports.parseSrcset = parseSrcset;
|
|
8417
|
-
module.exports.
|
|
9020
|
+
module.exports.tokenize = tokenize;
|