From f6712ec43ae7b5f049311d72372dfd676546e968 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 12 May 2022 18:55:29 +0100 Subject: [PATCH] fix(tracing): workaround chromium scrollTop slowness (#14114) References #14037. We used to query and save all non-zero `scrollTop`/`scrollLeft` to restore the page perfectly. However, this became very slow with Chromium v100 regression (see issue). This change only queries and stores `scrollTop`/`scrollLeft` for elements that are ancestors of the action target. The resulting snapshot does not perfectly recreate the original screen, but should be good enough to inspect the action. --- .../trace/recorder/snapshotterInjected.ts | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts index 03f9b86543..4b8798171d 100644 --- a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts +++ b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts @@ -44,6 +44,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string) { const kScrollTopAttribute = '__playwright_scroll_top_'; const kScrollLeftAttribute = '__playwright_scroll_left_'; const kStyleSheetAttribute = '__playwright_style_sheet_'; + const kTargetAttribute = '__playwright_target__'; // Symbols for our own info on Nodes/StyleSheets. const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_'); @@ -273,6 +274,28 @@ export function frameSnapshotStreamer(snapshotStreamer: string) { // Ensure we are up to date. this._handleMutations(this._observer.takeRecords()); + // Restore scroll positions for all ancestors of action target elements + // that will show the highlight/red dot in the trace viewer. + // Workaround for chromium regression: + // https://bugs.chromium.org/p/chromium/issues/detail?id=1324419 + // https://github.com/microsoft/playwright/issues/14037 + // TODO: remove after chromium is fixed? + const elementsToRestoreScrollPosition = new Set(); + const findElementsToRestoreScrollPositionRecursively = (element: Element) => { + let shouldAdd = element.hasAttribute(kTargetAttribute); + for (let child = element.firstElementChild; child; child = child.nextElementSibling) + shouldAdd = shouldAdd || findElementsToRestoreScrollPositionRecursively(child); + if (element.shadowRoot) { + for (let child = element.shadowRoot.firstElementChild; child; child = child.nextElementSibling) + shouldAdd = shouldAdd || findElementsToRestoreScrollPositionRecursively(child); + } + if (shouldAdd) + elementsToRestoreScrollPosition.add(element); + return shouldAdd; + }; + if (document.documentElement) + findElementsToRestoreScrollPositionRecursively(document.documentElement); + const visitNode = (node: Node | ShadowRoot): { equals: boolean, n: NodeSnapshot } | undefined => { const nodeType = node.nodeType; const nodeName = nodeType === Node.DOCUMENT_FRAGMENT_NODE ? 'template' : node.nodeName; @@ -380,12 +403,12 @@ export function frameSnapshotStreamer(snapshotStreamer: string) { expectValue(value); attrs[kSelectedAttribute] = value; } - if (element.scrollTop) { + if (elementsToRestoreScrollPosition.has(element) && element.scrollTop) { expectValue(kScrollTopAttribute); expectValue(element.scrollTop); attrs[kScrollTopAttribute] = '' + element.scrollTop; } - if (element.scrollLeft) { + if (elementsToRestoreScrollPosition.has(element) && element.scrollLeft) { expectValue(kScrollLeftAttribute); expectValue(element.scrollLeft); attrs[kScrollLeftAttribute] = '' + element.scrollLeft;