| 486 | } |
| 487 | |
| 488 | #flushText(): void { |
| 489 | if (this.#textStartIdx === -1) return; |
| 490 | |
| 491 | const content = this.#textPartial + |
| 492 | this.#buffer.slice(this.#textStartIdx, this.#bufferIndex); |
| 493 | this.#textStartIdx = -1; |
| 494 | this.#textPartial = ""; |
| 495 | |
| 496 | if (content.length === 0) return; |
| 497 | |
| 498 | // Reject illegal literal characters. |
| 499 | // The position-tracking path already validates inline in |
| 500 | // #captureText, so this native regex pre-check only runs for the |
| 501 | // no-position-tracking path (avoids redundant scan of every text |
| 502 | // node when positions are tracked). |
| 503 | if (!this.#trackPosition) { |
| 504 | const illegalRegex = this.#xml11 |
| 505 | ? ILLEGAL_XML_11_CHAR_REGEXP |
| 506 | : ILLEGAL_XML_CHAR_REGEXP; |
| 507 | if (illegalRegex.test(content)) { |
| 508 | for (let i = 0; i < content.length; i++) { |
| 509 | const code = content.charCodeAt(i); |
| 510 | if (this.#isIllegalLiteralChar(code)) { |
| 511 | this.#error( |
| 512 | `Illegal XML character U+${ |
| 513 | code.toString(16).toUpperCase().padStart(4, "0") |
| 514 | }`, |
| 515 | ); |
| 516 | } |
| 517 | } |
| 518 | } |
| 519 | } |
| 520 | // XML 1.0 §2.4: "]]>" is not allowed in text content. |
| 521 | // Catches both within-chunk and cross-chunk occurrences since |
| 522 | // #textPartial accumulates across chunks. |
| 523 | if (content.includes("]]>")) { |
| 524 | this.#error("Cannot use ']]>' in text content (XML 1.0 §2.4)"); |
| 525 | } |
| 526 | this.#xmlDeclAllowed = false; |
| 527 | this.#callbacks.onText?.( |
| 528 | content, |
| 529 | this.#textStartLine, |
| 530 | this.#textStartColumn, |
| 531 | this.#textStartOffset, |
| 532 | ); |
| 533 | } |
| 534 | |
| 535 | #getAttrValue(): string { |
| 536 | const value = this.#attrPartial + |