|
4 | 4 | const MAX_DOM_MARKERS = 120 |
5 | 5 | const MAX_MUTATION_COUNT = 5000 |
6 | 6 | const MAX_RESOURCE_COUNT = 1500 |
| 7 | + const MAX_PENDING_MUTATION_NODES = 200 |
7 | 8 | const SEND_DELAY = 900 |
8 | 9 | const MUTATION_BURST_WINDOW_MS = 1000 |
9 | | - const MUTATION_BURST_THRESHOLD = 300 |
| 10 | + const MUTATION_BURST_THRESHOLD = 150 |
10 | 11 | const MUTATION_COOLDOWN_MS = 5000 |
| 12 | + const MUTATION_FLUSH_DELAY = 200 |
11 | 13 | const CONTEXT_INVALIDATED_PATTERN = /extension context invalidated|context invalidated/i |
12 | 14 | const OBSERVER_INSTANCE_KEY = '__stackPrismContentObserver__' |
13 | 15 | const SKIP_TAGS = new Set(['VIDEO', 'AUDIO', 'CANVAS', 'PICTURE', 'SOURCE', 'TRACK', 'SVG', 'IMG']) |
14 | 16 | const SKIP_INITIATOR_TYPES = new Set(['img', 'video', 'audio', 'beacon', 'track', 'object', 'embed', 'css']) |
15 | 17 | const SKIP_RESOURCE_EXT = /\.(ts|m4s|mp4|webm|mov|m3u8|mpd|jpg|jpeg|png|gif|webp|avif|ico|woff2?|ttf|otf|eot)(\?.*)?$/i |
16 | | - const SKIP_CONTAINER_NAMES = [ |
17 | | - /danmaku/i, |
18 | | - /bullet[\s_-]*(comment|screen|chat)/i, |
19 | | - /barrage/i, |
20 | | - /(^|[\s._#-])chat([\s._#-]|$)/i, |
21 | | - /chat-?(panel|area|list|box|room|stream|window)/i, |
22 | | - /live-?chat/i, |
23 | | - /comment-?(stream|live|list)/i, |
24 | | - /(^|[\s._-])feed([\s._-]|$)/i, |
25 | | - /webcast/i |
26 | | - ] |
| 18 | + const SKIP_CONTAINER_PATTERN = |
| 19 | + /danmaku|bullet[\s_-]*(?:comment|screen|chat)|barrage|(?:^|[\s._#-])chat(?:[\s._#-]|$)|chat-?(?:panel|area|list|box|room|stream|window)|live-?chat|comment-?(?:stream|live|list)|(?:^|[\s._-])feed(?:[\s._-]|$)|webcast/i |
27 | 20 | const state = { |
28 | 21 | startedAt: Date.now(), |
29 | 22 | updatedAt: Date.now(), |
|
218 | 211 | 'script[src], link[href], iframe[src], [data-v-app], [ng-version], [data-reactroot], [data-turbo], [data-controller], astro-island, astro-slot' |
219 | 212 |
|
220 | 213 | const matchesSkipContainer = element => { |
221 | | - const tokens = [] |
222 | 214 | const id = element.id |
223 | | - if (id) tokens.push(id) |
| 215 | + if (id && SKIP_CONTAINER_PATTERN.test(id)) return true |
224 | 216 | const className = typeof element.className === 'string' ? element.className : element.getAttribute?.('class') || '' |
225 | | - if (className) { |
226 | | - for (const piece of className.split(/\s+/)) if (piece) tokens.push(piece) |
227 | | - } |
228 | | - if (!tokens.length) return false |
229 | | - for (const re of SKIP_CONTAINER_NAMES) { |
230 | | - for (const token of tokens) if (re.test(token)) return true |
231 | | - } |
232 | | - return false |
| 217 | + return Boolean(className && SKIP_CONTAINER_PATTERN.test(className)) |
233 | 218 | } |
234 | 219 |
|
235 | 220 | const collectFromElement = element => { |
|
343 | 328 | stopped = true |
344 | 329 | clearTimeout(sendTimer) |
345 | 330 | if (pendingMutationFrame) { |
346 | | - if (typeof cancelIdleCallback === 'function') { |
347 | | - cancelIdleCallback(pendingMutationFrame) |
348 | | - } else { |
349 | | - clearTimeout(pendingMutationFrame) |
350 | | - } |
| 331 | + clearTimeout(pendingMutationFrame) |
351 | 332 | pendingMutationFrame = 0 |
352 | 333 | } |
353 | 334 | pendingMutationNodes = [] |
|
431 | 412 | const nodes = pendingMutationNodes |
432 | 413 | pendingMutationNodes = [] |
433 | 414 | if (!nodes.length) return |
434 | | - const processed = [] |
435 | 415 | let changed = false |
436 | 416 | for (const node of nodes) { |
437 | 417 | if (!node.isConnected) continue |
438 | | - let containedByAncestor = false |
439 | | - for (let i = 0; i < processed.length; i++) { |
440 | | - if (processed[i].contains(node)) { |
441 | | - containedByAncestor = true |
442 | | - break |
443 | | - } |
444 | | - } |
445 | | - if (containedByAncestor) continue |
446 | | - processed.push(node) |
447 | 418 | changed = collectFromElement(node) || changed |
448 | 419 | } |
449 | 420 | if (changed) { |
|
454 | 425 |
|
455 | 426 | const scheduleMutationFlush = () => { |
456 | 427 | if (pendingMutationFrame || stopped) return |
457 | | - if (typeof requestIdleCallback === 'function') { |
458 | | - pendingMutationFrame = requestIdleCallback(processPendingMutationNodes, { timeout: 1000 }) |
459 | | - } else { |
460 | | - pendingMutationFrame = setTimeout(processPendingMutationNodes, 200) |
| 428 | + pendingMutationFrame = setTimeout(processPendingMutationNodes, MUTATION_FLUSH_DELAY) |
| 429 | + } |
| 430 | + |
| 431 | + const triggerMutationCooldown = now => { |
| 432 | + mutationCooldownUntil = now + MUTATION_COOLDOWN_MS |
| 433 | + pendingMutationNodes = [] |
| 434 | + if (pendingMutationFrame) { |
| 435 | + clearTimeout(pendingMutationFrame) |
| 436 | + pendingMutationFrame = 0 |
461 | 437 | } |
462 | 438 | } |
463 | 439 |
|
|
471 | 447 | mutationBurstWindowStart = now |
472 | 448 | mutationBurstCount = 0 |
473 | 449 | } |
474 | | - for (const mutation of mutations) { |
| 450 | + let pendingFull = false |
| 451 | + outer: for (const mutation of mutations) { |
475 | 452 | for (const node of mutation.addedNodes) { |
476 | 453 | if (node.nodeType !== Node.ELEMENT_NODE) continue |
477 | 454 | if (SKIP_TAGS.has(node.tagName)) continue |
478 | 455 | if (matchesSkipContainer(node)) continue |
479 | 456 | state.mutationCount += 1 |
480 | 457 | mutationBurstCount += 1 |
481 | 458 | pendingMutationNodes.push(node) |
| 459 | + if (pendingMutationNodes.length >= MAX_PENDING_MUTATION_NODES) { |
| 460 | + pendingFull = true |
| 461 | + break outer |
| 462 | + } |
482 | 463 | } |
483 | 464 | } |
484 | | - if (mutationBurstCount >= MUTATION_BURST_THRESHOLD) { |
485 | | - mutationCooldownUntil = now + MUTATION_COOLDOWN_MS |
486 | | - pendingMutationNodes = [] |
487 | | - if (pendingMutationFrame) { |
488 | | - if (typeof cancelIdleCallback === 'function') cancelIdleCallback(pendingMutationFrame) |
489 | | - else clearTimeout(pendingMutationFrame) |
490 | | - pendingMutationFrame = 0 |
491 | | - } |
| 465 | + if (pendingFull || mutationBurstCount >= MUTATION_BURST_THRESHOLD) { |
| 466 | + triggerMutationCooldown(now) |
492 | 467 | return |
493 | 468 | } |
494 | 469 | if (state.mutationCount >= MAX_MUTATION_COUNT) { |
|
0 commit comments