fix(trace-viewer): multiple iframe and UX fixes (#10486)

This commit is contained in:
Pavel Feldman 2021-11-23 11:36:18 -08:00 committed by GitHub
parent 5a8010cf4f
commit 206a877cea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 34 additions and 17 deletions

View File

@ -265,6 +265,12 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
return;
if (nodeName === 'SCRIPT')
return;
// Don't preload resources.
if (nodeName === 'LINK' && nodeType === Node.ELEMENT_NODE) {
const rel = (node as Element).getAttribute('rel')?.toLowerCase();
if (rel === 'preload' || rel === 'prefetch')
return;
}
if (this._removeNoScript && nodeName === 'NOSCRIPT')
return;
if (nodeName === 'META' && (node as HTMLMetaElement).httpEquiv.toLowerCase() === 'content-security-policy')

View File

@ -59,8 +59,12 @@ export class SnapshotRenderer {
// Element node.
const builder: string[] = [];
builder.push('<', n[0]);
for (const [attr, value] of Object.entries(n[1] || {}))
builder.push(' ', attr, '="', escapeAttribute(value as string), '"');
// Never set relative URLs as <iframe src> - they start fetching frames immediately.
const isFrame = n[0] === 'IFRAME' || n[0] === 'FRAME';
for (const [attr, value] of Object.entries(n[1] || {})) {
const attrToSet = isFrame && attr.toLowerCase() === 'src' ? '__playwright_src__' : attr;
builder.push(' ', attrToSet, '="', escapeAttribute(value as string), '"');
}
builder.push('>');
for (let i = 2; i < n.length; i++)
builder.push(visit(n[i], snapshotIndex));
@ -181,14 +185,19 @@ function snapshotScript() {
scrollLefts.push(e);
for (const iframe of root.querySelectorAll('iframe')) {
const src = iframe.getAttribute('src');
const src = iframe.getAttribute('__playwright_src__');
if (!src) {
iframe.setAttribute('src', 'data:text/html,<body style="background: #ddd"></body>');
} else {
// Append query parameters to inherit ?name= or ?time= values from parent.
const url = new URL('/trace' + src + window.location.search, window.location.href);
const url = new URL(window.location.href);
url.searchParams.delete('pointX');
url.searchParams.delete('pointY');
// We can be loading iframe from within iframe, reset base to be absolute.
const index = url.pathname.lastIndexOf('/snapshot/');
if (index !== -1)
url.pathname = url.pathname.substring(0, index + 1);
url.pathname += src.substring(1);
iframe.setAttribute('src', url.toString());
}
}

View File

@ -88,7 +88,9 @@ export class SnapshotServer {
headers.delete('Content-Length');
headers.set('Content-Length', String(content.size));
headers.set('Cache-Control', 'public, max-age=31536000');
return new Response(content, {
const { status } = resource.response;
const isNullBodyStatus = status === 101 || status === 204 || status === 205 || status === 304;
return new Response(isNullBodyStatus ? null : content, {
headers,
status: resource.response.status,
statusText: resource.response.statusText,

View File

@ -70,9 +70,11 @@
padding: 4px;
border-radius: 3px;
height: 28px;
display: flex;
align-items: center;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: block;
flex: none;
}
.snapshot-container {

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
import { Size } from '../geometry';
import './snapshotTab.css';
import './tabbedPane.css';
import * as React from 'react';
@ -23,8 +22,7 @@ import { ActionTraceEvent } from '../../../server/trace/common/traceEvents';
export const SnapshotTab: React.FunctionComponent<{
action: ActionTraceEvent | undefined,
defaultSnapshotInfo: { viewport: Size, url: string },
}> = ({ action, defaultSnapshotInfo }) => {
}> = ({ action }) => {
const [measure, ref] = useMeasure<HTMLDivElement>();
const [snapshotIndex, setSnapshotIndex] = React.useState(0);
@ -57,7 +55,7 @@ export const SnapshotTab: React.FunctionComponent<{
}, [snapshotIndex, snapshots]);
const iframeRef = React.useRef<HTMLIFrameElement>(null);
const [snapshotInfo, setSnapshotInfo] = React.useState(defaultSnapshotInfo);
const [snapshotInfo, setSnapshotInfo] = React.useState({ viewport: kDefaultViewport, url: '' });
React.useEffect(() => {
(async () => {
if (snapshotInfoUrl) {
@ -66,8 +64,7 @@ export const SnapshotTab: React.FunctionComponent<{
if (!info.error)
setSnapshotInfo(info);
} else {
// Reset to default if snapshotInfoUrl was removed
setSnapshotInfo(defaultSnapshotInfo);
setSnapshotInfo({ viewport: kDefaultViewport, url: '' });
}
if (!iframeRef.current)
return;
@ -76,7 +73,7 @@ export const SnapshotTab: React.FunctionComponent<{
} catch (e) {
}
})();
}, [iframeRef, snapshotUrl, snapshotInfoUrl, pointX, pointY, defaultSnapshotInfo]);
}, [iframeRef, snapshotUrl, snapshotInfoUrl, pointX, pointY]);
const snapshotSize = snapshotInfo.viewport;
const scale = Math.min(measure.width / snapshotSize.width, measure.height / snapshotSize.height, 1);
@ -124,3 +121,5 @@ function renderTitle(snapshotTitle: string): string {
return 'Action';
return snapshotTitle;
}
const kDefaultViewport = { width: 1280, height: 720 };

View File

@ -115,7 +115,6 @@ export const Workbench: React.FunctionComponent<{
})();
}, [traceURL, uploadedTraceName]);
const defaultSnapshotInfo = { viewport: contextEntry.options.viewport || { width: 1280, height: 720 }, url: '' };
const boundaries = { minimum: contextEntry.startTime, maximum: contextEntry.endTime };
@ -153,7 +152,7 @@ export const Workbench: React.FunctionComponent<{
</div>
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
<SplitView sidebarSize={300} orientation='horizontal'>
<SnapshotTab action={selectedAction} defaultSnapshotInfo={defaultSnapshotInfo} />
<SnapshotTab action={selectedAction} />
<TabbedPane tabs={tabs} selectedTab={selectedPropertiesTab} setSelectedTab={setSelectedPropertiesTab}/>
</SplitView>
<TabbedPane tabs={

View File

@ -142,7 +142,7 @@ it.describe('snapshots', () => {
for (; ; ++counter) {
snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot' + counter);
const text = distillSnapshot(snapshot).replace(/frame@[^"]+["]/, '<id>"');
if (text === '<IFRAME src=\"/snapshot/<id>\"></IFRAME>')
if (text === '<IFRAME __playwright_src__=\"/snapshot/<id>\"></IFRAME>')
break;
await page.waitForTimeout(250);
}