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

Open
wants to merge 5 commits into
base: trunk
Choose a base branch
from

Conversation

adamziel
Copy link
Collaborator

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

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
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
Status: Reviewed
Development

Successfully merging this pull request may close these issues.

3 participants