Skip to content
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

[Query API] Use the exact redirect URL provided in the ?url= query param #1945

Merged
merged 9 commits into from
Nov 15, 2024

Conversation

adamziel
Copy link
Collaborator

@adamziel adamziel commented Oct 25, 2024

This PR:

  • Replaces the wp_redirect() call with header("Location: ") to ensure support for all valid redirection URLs.
  • Adds a timeout before probing for the embedded Playground iframe URL when the load event handler runs.

Ditching wp_redirect()

The wp_redirect() call was introduced in #1856 to handle the post-autologin redirect. Unfortunately, it strips valid sequences such as %0A and %0D from the redirection URL. This isn't useful in Playground and breaks valid use-cases such as using the ?url= parameter to initialize html-api-debugger with a valid HTML markup containing newlines.

Timeout before probing contentWindow.location.href

When navigating to a page with %0A sequences (encoded newlines) in the query string, the location.href property of the iframe's content window doesn't seem to reflect them. Everything else is in place, but not the %0A sequences.

Weirdly, these sequences are available after the next event loop tick – hence the setTimeout(0).

The exact cause is unclear to me. The WHATWG HTML Standard [1] has a few hints:

  • Current and active session history entries may get out of sync for iframes.
  • Documents inside iframes have "is delaying load events" set to true.

But there doesn't seem to be any concrete explanation and no recommended remediation. If anyone has a clue, please share it in a GitHub issue or start a new PR.

[1] https://html.spec.whatwg.org/multipage/document-sequences.html#nav-active-history-entry

Testing instructions

CI tests aside, try this:

  1. Go to http://localhost:5400/website-server/?plugin=html-api-debugger&url=%2Fwp-admin%2Fadmin.php%3Fpage%3Dhtml-api-debugger%26html%3D%253Cdiv%253E%250A1%250A2%250A3%250A%253C%252Fdiv%253E&wp=6.7&php=8.0&networking=no&language=&multisite=no&random=pf3oq1y3vx
  2. Confirm the "address bar" on the Playground page reflects the actual, correct URL: /wp-admin/admin.php?page=html-api-debugger&html=%3Cdiv%3E%0A1%0A2%0A3%0A%3C%2Fdiv%3E
  3. Confirm you can see the following HTML
<div>
1
2
3
</div>

cc @sirreal @bgrgicak

This PR:

* Replaces the `wp_redirect()` call with `header("Location: ")` to ensure
support for all valid redirection URLs.
* Adds a timeout before probing for the embedded Playground `iframe` URL
  when the `load` event handler runs.

 ### Ditching `wp_redirect()`

The `wp_redirect()` call was introduced in #1856 to handle the post-autologin
redirect. Unfortunately, it strips valid sequences such as `%0A` and `%0D` from
the redirection URL. This isn't useful in Playground and breaks valid
use-cases such as using the `?url=` parameter to initialize `html-api-debugger`
with a valid HTML markup containing newlines.

 ### Timeout before probing `contentWindow.location.href`

When navigating to a page with %0A sequences (encoded newlines)
in the query string, the `location.href` property of the
iframe's content window doesn't seem to reflect them. Everything
else is in place, but not the %0A sequences.

Weirdly, these sequences are available after the next event
loop tick – hence the `setTimeout(0)`.

The exact cause is unclear to me. The WHATWG HTML Standard [1] has a few hints:

* Current and active session history entries may get out of
  sync for iframes.
* Documents inside iframes have "is delaying load events" set
  to true.

But there doesn't seem to be any concrete explanation and no
recommended remediation. If anyone has a clue, please share it
in a GitHub issue or start a new PR.

[1] https://html.spec.whatwg.org/multipage/document-sequences.html#nav-active-history-entry

 ## Testing instructions

CI tests aside, try this:

1. Go to http://localhost:5400/website-server/?plugin=html-api-debugger&url=%2Fwp-admin%2Fadmin.php%3Fpage%3Dhtml-api-debugger%26html%3D%253Cdiv%253E%250A1%250A2%250A3%250A%253C%252Fdiv%253E&wp=6.6&php=8.0&networking=no&language=&multisite=no&random=pf3oq1y3vx
2. Confirm the "address bar" on the Playground page reflects the actual,
   correct URL:
   `/wp-admin/admin.php?page=html-api-debugger&html=%3Cdiv%3E%0A1%0A2%0A3%0A%3C%2Fdiv%3E`
3. Confirm you can see the following HTML

```
<div>
1
2
3
</div>
```

cc @sirreal @bgrgicak
@adamziel adamziel added [Type] Bug An existing feature does not function as intended [Package][@wp-playground] Blueprints [Aspect] Website labels Oct 25, 2024
@adamziel adamziel requested a review from a team as a code owner October 25, 2024 17:19
*/
// Get the content window while e.currentTarget is available.
// It will be undefined on the next event loop tick.
const contentWindow = e.currentTarget!.contentWindow;
Copy link
Member

Choose a reason for hiding this comment

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

Could this be related to the async event handler?

I've done some testing (not on playground) and with an async event handler the behavior seemed less reliable. However, with the sync event handler, e.target.contentWindow.location.href seemed to reliably be the expected value.

I don't know whether target/currentTarget matters, I suspect not, but target seemed to work well in my testing the load event on the iframe.

Copy link
Collaborator Author

@adamziel adamziel Nov 4, 2024

Choose a reason for hiding this comment

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

Yeah, the information is lost after the await. I was thinking it's related to React and how it clears currentTarget after the synchronous event handler returns, but now that I think about it, React shouldn't be in the mix here. You may be right about the target/currentTarget – was the latter affected by bubbling?

@bgrgicak bgrgicak self-requested a review October 28, 2024 06:26
// Get the content window while e.currentTarget is available.
// It will be undefined on the next event loop tick.
const contentWindow = e.currentTarget!.contentWindow;
await new Promise((resolve) => setTimeout(resolve, 0));
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't work in Safari and Firefox.
Locally in Safari, it started working when I increased the timeout to 10, but that doesn't mean the exact timeout will always work.

Copy link
Collaborator Author

@adamziel adamziel Oct 28, 2024

Choose a reason for hiding this comment

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

Good spot! I wonder what's the root cause of this behavior. A setInterval and polling twice per second would likely solve this, but I'm hesitant of just throwing it in without fully understanding the problem.

@adamziel
Copy link
Collaborator Author

Oh wow, synchronizing navigables histories is an entire thing:

https://html.spec.whatwg.org/dev/browsing-the-web.html#centralized-modifications-of-session-history

@adamziel
Copy link
Collaborator Author

What that boils down to is:

  • Synchronizing URLs across navigables involves a sophisticated algorithm.
  • There's no specific event we can listen on and the timing may vary.
  • Detecting URL changes requires either polling in the parent context or calling postMessage() from the nested context.

@bgrgicak bgrgicak self-requested a review November 5, 2024 06:17
Comment on lines +127 to +129
// We need to use the html-api-debugger plugin to test this because
// most wp-admin pages enforce a redirect to a sanitized (broken)
// version of the URL.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this because of the login redirect Playground does, or is it a WordPress redirect that sanitizes the URL?

home_url($redirect_url),
302
);
$redirect_url = home_url($redirect_url);
Copy link
Collaborator

Choose a reason for hiding this comment

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

@adamziel we don't need to force this to a absolute URL anymore because we have a a fix for the relative URL redirects in Safari.

Suggested change
$redirect_url = home_url($redirect_url);

Copy link
Collaborator

Choose a reason for hiding this comment

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

I hope that you don't mind, that I merged trunk into your PR and resolved the conflict with the Safari redirect fix.

@bgrgicak bgrgicak self-requested a review November 14, 2024 19:48
Copy link
Collaborator

@bgrgicak bgrgicak left a comment

Choose a reason for hiding this comment

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

@adamziel your test blueprint is throwing a boot error because of the HTML in the URL. I also checked trunk an the same is happening.

@adamziel
Copy link
Collaborator Author

adamziel commented Nov 14, 2024

Thanks for rebasing @bgrgicak! The test link is failing indeed, not good!

your test blueprint is throwing a boot error because of the HTML in the URL

I'm confused by the "because of the HTML in the URL" part. What do you mean? How is that causing a failure?

@adamziel
Copy link
Collaborator Author

adamziel commented Nov 14, 2024

Bumping wp=6.6 to wp=6.7 in the test URL solved the error, I assume the latest HTML debugger plugin isn't backwards compatible with WordPress 6.7. Can you confirm @sirreal ?

@bgrgicak
Copy link
Collaborator

I'm confused by the "because of the HTML in the URL" part. What do you mean? How is that causing a failure?

Ignore that part, when I tested the blueprint, removing the html=%3Cdiv%3E%0A1%0A2%0A3%0A%3C%2Fdiv%3E argument seemed to make it work for me on Playground.WordPress.net, but it's unrelated.

Later I also realized it was related to the WP versions, but it was late and I wanted to do more research in the morning before commenting anything.

I ran the Webkit test locally a couple of times and it passes locally.

@sirreal
Copy link
Member

sirreal commented Nov 15, 2024

I assume the latest HTML debugger plugin isn't backwards compatible with WordPress 6.7. Can you confirm @sirreal ?

Its minimum version was changed to latest (6.7) and does rely on new functionality. Sorry it broke the test!

@adamziel
Copy link
Collaborator Author

adamziel commented Nov 15, 2024

I've disabled that e2e test also in Safari. Let's get the fix in and keep monitoring the problem in Firefox and Safari. It's such a weird issue with close to no reading material out there and I can't think of any other solution anyway.

@adamziel adamziel merged commit 7530f12 into trunk Nov 15, 2024
9 of 10 checks passed
@adamziel adamziel deleted the fix-query-api-redirects branch November 15, 2024 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Aspect] Website [Package][@wp-playground] Blueprints [Type] Bug An existing feature does not function as intended
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants