Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat(trace-viewer): render iframe canvas in trace viewer #33809

Merged
merged 2 commits into from
Dec 13, 2024

Conversation

ruifigueira
Copy link
Contributor

Closes: #33779

This comment has been minimized.

@ruifigueira ruifigueira force-pushed the feat/tv-iframe-canvas branch from 9e96691 to ddc34a7 Compare December 3, 2024 08:24

This comment has been minimized.

Copy link
Member

@Skn0tt Skn0tt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks interesting - i'll need to look with a little more time though next week. I left one small question already from my first cursory glance :)

@@ -249,6 +272,10 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
const targetElements: Element[] = [];
const canvasElements: HTMLCanvasElement[] = [];

let topFrameWindow: Window = window;
while (topFrameWindow !== topFrameWindow.parent && !topFrameWindow.location.pathname.match(/\/page@[a-z0-9]+$/))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if you remove the second condition here? I would have assumed that walking up window.parent should lead you to the top frame without any other conditions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may not be the actual top window. In fact, it's only the top window when we open the snapshot in a new tab. In trace viewer / UI mode, it must be the snapshot root window, which is an iframe:

image

Maybe topFrameWindow should be called topSnapshotWindow instead.

@yury-s yury-s requested a review from Skn0tt December 11, 2024 00:02
Copy link
Member

@Skn0tt Skn0tt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gave it another look. My gut feeling says that we should try to perform as much heavy lifting at snapshot time as possible. We already caught a bug about that in the past: #33575

Also left a question to verify my understanding of the code.

viewport,
frames: new WeakMap(),
};
window['__playwright_canvas_render_info__'] = canvasRenderInfo;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after reading through this, my impression is that there's a non-trivial order of execution here, where snapshotScript is being executed for all frames in the frame, and they all read each other's window.__playwright_canvas_render_info__ states. Is this understanding right?

Copy link
Contributor Author

@ruifigueira ruifigueira Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right. Each parent window, before rendering each iframe, extracts the __playwright_canvas_render_info__ value and keeps in that variable. It then removes the attribute and renders the element normally, which will eventually trigger recursively the same process inside the iframe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, that makes sense. Could you add a comment about that somewhere, for future readers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, would __playwright_frame_bounding_rects__ be a more fitting name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about this suggestion I gave on #33809 (comment) ?

Maybe topFrameWindow should be called topSnapshotWindow instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good as well!

const value = JSON.stringify({
left: boundingRect.left / window.innerWidth,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By moving this division to the rendering stage, we make an implicit assumption that window.innerWidth will be the same at snapshot time and at rendering time. I'm not sure that's true. Do you think we could perform all the heavy lifting at snapshot time instead? That way, we can be more sure that the bounding rect actually refers to the right pixels in the screenshot.

Copy link
Contributor Author

@ruifigueira ruifigueira Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of keeping that at snapshot time, but the problem is that most likely we'll hit on XSS issues. I moved those computations to rendering stage because at that point we'll no longer have XSS issues (all rendered frames are under the same domain).

Nevertheless, the end result should be the same, because I capture window.innerWidth for each frame and save it in its __playwright_bounding_rect__ attribute and that's the value I'm using to compute the ratio

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevertheless, the end result should be the same, because I capture window.innerWidth for each frame and save it in its playwright_bounding_rect attribute.

That makes total sense to me, and resolves my worry about the render-time differences. nice!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I said was not 100% correct: actually the window.inner{Width,Height} is not from iframe's __playwright_bounding_rect__ , it was already being captured at snapshot time:

viewport: {
width: window.innerWidth,
height: window.innerHeight,
},

and those are the dimensions I use at rendering time. That's why I changed the snapshotScript signature to receive the viewport structure.

Copy link
Contributor

Test results for "tests 1"

2 flaky ⚠️ [playwright-test] › ui-mode-test-setup.spec.ts:98:5 › should show errors in config @macos-latest-node18-1
⚠️ [playwright-test] › ui-mode-test-watch.spec.ts:145:5 › should watch all @windows-latest-node18-1

37248 passed, 650 skipped
✔️✔️✔️

Merge workflow run.

Copy link
Member

@Skn0tt Skn0tt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks so much for your effort on this.

@Skn0tt Skn0tt merged commit c700a84 into microsoft:main Dec 13, 2024
27 of 29 checks passed
@ruifigueira ruifigueira deleted the feat/tv-iframe-canvas branch December 26, 2024 17:17
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature]: render iframe canvas in trace viewer
2 participants