forked from rrweb-io/rrweb
-
Notifications
You must be signed in to change notification settings - Fork 1
RRWeb sync splitCssPerf #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
d719821
Fix performance of splitCssText (#1615)
eoghanmurray 8ae547f
Fix splitCssText again (#1640)
eoghanmurray b7d971b
Update changeset to match package names
jpollock-ampl f84a96d
Merge branch 'master' into rrweb-sync-split-css-perf
jpollock-ampl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "rrweb-snapshot": patch | ||
| "rrweb": patch | ||
jxiwang marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| --- | ||
|
|
||
| Improve performance of splitCssText for <style> elements with large css content - see #1603 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "rrweb-snapshot": patch | ||
| "rrweb": patch | ||
| --- | ||
|
|
||
| Improve performance of splitCssText for <style> elements with large css content - see #1603 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,7 +80,7 @@ | |
| * Browsers sometimes incorrectly escape `@import` on `.cssText` statements. | ||
| * This function tries to correct the escaping. | ||
| * more info: https://bugs.chromium.org/p/chromium/issues/detail?id=1472259 | ||
| * @param cssImportRule | ||
|
Check warning on line 83 in packages/rrweb-snapshot/src/utils.ts
|
||
| * @returns `cssText` with browser inconsistencies fixed, or null if not applicable. | ||
| */ | ||
| export function escapeImportStatement(rule: CSSImportRule): string { | ||
|
|
@@ -450,42 +450,132 @@ | |
| * Intention is to normalize by remove spaces, semicolons and CSS comments | ||
| * so that we can compare css as authored vs. output of stringifyStylesheet | ||
| */ | ||
| export function normalizeCssString(cssText: string): string { | ||
| return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, ''); | ||
| export function normalizeCssString( | ||
| cssText: string, | ||
| /** | ||
| * _testNoPxNorm: only used as part of the 'substring matching going from many to none' | ||
| * test case so that it will trigger a failure if the conditions that let to the creation of that test arise again | ||
| */ | ||
| _testNoPxNorm = false, | ||
| ): string { | ||
| if (_testNoPxNorm) { | ||
| return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, ''); | ||
| } else { | ||
| return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, '').replace(/0px/g, '0'); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Maps the output of stringifyStylesheet to individual text nodes of a <style> element | ||
| * performance is not considered as this is anticipated to be very much an edge case | ||
| * (javascript is needed to add extra text nodes to a <style>) | ||
| * which occurs when javascript is used to append to the style element | ||
| * and may also occur when browsers opt to break up large text nodes | ||
| * performance needs to be considered, see e.g. #1603 | ||
| */ | ||
| export function splitCssText( | ||
| cssText: string, | ||
| style: HTMLStyleElement, | ||
| _testNoPxNorm = false, | ||
| ): string[] { | ||
| const childNodes = Array.from(style.childNodes); | ||
| const splits: string[] = []; | ||
| let iterCount = 0; | ||
| if (childNodes.length > 1 && cssText && typeof cssText === 'string') { | ||
| const cssTextNorm = normalizeCssString(cssText); | ||
| let cssTextNorm = normalizeCssString(cssText, _testNoPxNorm); | ||
| const normFactor = cssTextNorm.length / cssText.length; | ||
| for (let i = 1; i < childNodes.length; i++) { | ||
| if ( | ||
| childNodes[i].textContent && | ||
| typeof childNodes[i].textContent === 'string' | ||
| ) { | ||
| const textContentNorm = normalizeCssString(childNodes[i].textContent!); | ||
| for (let j = 3; j < textContentNorm.length; j++) { | ||
| // find a substring that appears only once | ||
| const bit = textContentNorm.substring(0, j); | ||
| if (cssTextNorm.split(bit).length === 2) { | ||
| const splitNorm = cssTextNorm.indexOf(bit); | ||
| const textContentNorm = normalizeCssString( | ||
| childNodes[i].textContent!, | ||
|
Check warning on line 491 in packages/rrweb-snapshot/src/utils.ts
|
||
| _testNoPxNorm, | ||
| ); | ||
| const jLimit = 100; // how many iterations for the first part of searching | ||
| let j = 3; | ||
| for (; j < textContentNorm.length; j++) { | ||
| if ( | ||
| // keep consuming css identifiers (to get a decent chunk more quickly) | ||
| textContentNorm[j].match(/[a-zA-Z0-9]/) || | ||
| // substring needs to be unique to this section | ||
| textContentNorm.indexOf(textContentNorm.substring(0, j), 1) !== -1 | ||
| ) { | ||
| continue; | ||
| } | ||
| break; | ||
| } | ||
| for (; j < textContentNorm.length; j++) { | ||
| let startSubstring = textContentNorm.substring(0, j); | ||
| // this substring should appears only once in overall text too | ||
| let cssNormSplits = cssTextNorm.split(startSubstring); | ||
| let splitNorm = -1; | ||
| if (cssNormSplits.length === 2) { | ||
| splitNorm = cssNormSplits[0].length; | ||
| } else if ( | ||
| cssNormSplits.length > 2 && | ||
| cssNormSplits[0] === '' && | ||
| childNodes[i - 1].textContent !== '' | ||
| ) { | ||
| // this childNode has same starting content as previous | ||
| splitNorm = cssTextNorm.indexOf(startSubstring, 1); | ||
| } else if (cssNormSplits.length === 1) { | ||
| // try to roll back to get multiple matches again | ||
| startSubstring = startSubstring.substring( | ||
| 0, | ||
| startSubstring.length - 1, | ||
| ); | ||
| cssNormSplits = cssTextNorm.split(startSubstring); | ||
| if (cssNormSplits.length <= 1) { | ||
| // no split possible | ||
| splits.push(cssText); | ||
| return splits; | ||
| } | ||
| j = jLimit + 1; // trigger end of search | ||
| } else if (j === textContentNorm.length - 1) { | ||
| // we're about to end loop without a split point | ||
| splitNorm = cssTextNorm.indexOf(startSubstring); | ||
| } | ||
| if (cssNormSplits.length >= 2 && j > jLimit) { | ||
| const prevTextContent = childNodes[i - 1].textContent; | ||
| if (prevTextContent && typeof prevTextContent === 'string') { | ||
| // pick the first matching point which respects the previous chunk's approx size | ||
| const prevMinLength = normalizeCssString(prevTextContent).length; | ||
| splitNorm = cssTextNorm.indexOf(startSubstring, prevMinLength); | ||
| } | ||
| if (splitNorm === -1) { | ||
| // fall back to pick the first matching point of many | ||
| splitNorm = cssNormSplits[0].length; | ||
| } | ||
| } | ||
| if (splitNorm !== -1) { | ||
| // find the split point in the original text | ||
| for (let k = splitNorm; k < cssText.length; k++) { | ||
| if ( | ||
| normalizeCssString(cssText.substring(0, k)).length === splitNorm | ||
| ) { | ||
| let k = Math.floor(splitNorm / normFactor); | ||
| for (; k > 0 && k < cssText.length; ) { | ||
| iterCount += 1; | ||
| if (iterCount > 50 * childNodes.length) { | ||
| // quit for performance purposes | ||
| splits.push(cssText); | ||
| return splits; | ||
| } | ||
| const normPart = normalizeCssString( | ||
| cssText.substring(0, k), | ||
| _testNoPxNorm, | ||
| ); | ||
| if (normPart.length === splitNorm) { | ||
| splits.push(cssText.substring(0, k)); | ||
| cssText = cssText.substring(k); | ||
| cssTextNorm = cssTextNorm.substring(splitNorm); | ||
| break; | ||
| } else if (normPart.length < splitNorm) { | ||
| k += Math.max( | ||
| 1, | ||
| Math.floor((splitNorm - normPart.length) / normFactor), | ||
| ); | ||
| } else { | ||
| k -= Math.max( | ||
| 1, | ||
| Math.floor((normPart.length - splitNorm) * normFactor), | ||
| ); | ||
| } | ||
| } | ||
| break; | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.