|
312 | 312 | let added = []; |
313 | 313 |
|
314 | 314 | let fragment = document.createDocumentFragment(); |
| 315 | + // [PATCH: findRef fast-path] Carry an indexOfRefs alongside the |
| 316 | + // fragment so callers' findElement(*, fragment) lookups hit the |
| 317 | + // fast path, and so the caller can merge this into dest.indexOfRefs |
| 318 | + // in one pass after dest.appendChild(fragment). Without this, every |
| 319 | + // findElement(rebuiltAncestor, dest) on a subsequent append() call |
| 320 | + // falls through to dest.querySelector("[data-ref='...']") — measured |
| 321 | + // as 15,767 dictMiss calls ≈ 2.5 s of render on the 1651-page book. |
| 322 | + fragment.indexOfRefs = {}; |
315 | 323 |
|
316 | 324 | // Handle rowspan on table |
317 | 325 | if (node.nodeName === "TR") { |
|
385 | 393 | } |
386 | 394 | added.push(parent); |
387 | 395 |
|
| 396 | + // [PATCH: findRef fast-path] Record the rebuilt clone in the |
| 397 | + // fragment's indexOfRefs so findElement(*, fragment) hits the |
| 398 | + // fast path and so dest.indexOfRefs can be updated cheaply by |
| 399 | + // the caller (see Layout.append's rebuild branch). |
| 400 | + if (parent.dataset && parent.dataset.ref) { |
| 401 | + fragment.indexOfRefs[parent.dataset.ref] = parent; |
| 402 | + } |
| 403 | + |
388 | 404 | // rebuild table rows |
389 | 405 | if (parent.nodeName === "TD" && ancestor.parentElement.contains(ancestor)) { |
390 | 406 | let td = ancestor; |
|
1716 | 1732 | } |
1717 | 1733 |
|
1718 | 1734 | dest.appendChild(fragment); |
| 1735 | + |
| 1736 | + // [PATCH: findRef fast-path] Merge the rebuilt fragment's |
| 1737 | + // indexOfRefs into dest's. Without this, every subsequent |
| 1738 | + // findElement(rebuiltAncestor, dest) on this page misses |
| 1739 | + // the dict and falls through to dest.querySelector. |
| 1740 | + if (fragment.indexOfRefs) { |
| 1741 | + if (!dest.indexOfRefs) dest.indexOfRefs = {}; |
| 1742 | + for (const k in fragment.indexOfRefs) { |
| 1743 | + dest.indexOfRefs[k] = fragment.indexOfRefs[k]; |
| 1744 | + } |
| 1745 | + } |
1719 | 1746 | } else { |
1720 | 1747 | dest.appendChild(clone); |
1721 | 1748 | } |
|
2587 | 2614 | false |
2588 | 2615 | ); |
2589 | 2616 |
|
| 2617 | + // [PATCH: findRef fast-path] Populate content.indexOfRefs as we walk |
| 2618 | + // so every later findElement(*, source) call hits the dict instead |
| 2619 | + // of falling through to source.querySelector("[data-ref='X']"), |
| 2620 | + // which scans the entire source DOM (thousands of nodes). Measured |
| 2621 | + // as 848 + 42 noDict calls in createBreakToken ≈ 1+ s of render on |
| 2622 | + // the 1651-page book. |
| 2623 | + if (!content.indexOfRefs) content.indexOfRefs = {}; |
| 2624 | + |
2590 | 2625 | let node = treeWalker.nextNode(); |
2591 | 2626 | while(node) { |
2592 | 2627 |
|
|
2602 | 2637 | // node.setAttribute("data-children", node.childNodes.length); |
2603 | 2638 |
|
2604 | 2639 | // node.setAttribute("data-text", node.textContent.trim().length); |
| 2640 | + |
| 2641 | + // [PATCH: findRef fast-path] record after data-ref is guaranteed. |
| 2642 | + content.indexOfRefs[node.getAttribute("data-ref")] = node; |
| 2643 | + |
2605 | 2644 | node = treeWalker.nextNode(); |
2606 | 2645 | } |
2607 | 2646 | } |
|
0 commit comments