PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.13
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.13
1.1.13 1.1.12 1.1.11 1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 All 37 releases
← All changes | includes/render/chromeless-bridge.php +105 -2 1.1.9 → 1.1.13 View file →
@@ -209,9 +209,26 @@
209 209 * it runs after every other admin script — a second or more after the
210 210 * browser painted the "Settings saved." notice the ring is
211 211 * confirming. From the head it beats the body to the screen.
212 212 *
213 - * The parent ignores it unless that window has a submit waiting.
213 + * It also says where the frame landed, for documents the shell cannot
214 + * read: one sent with `Document-Isolation-Policy`, as WordPress sends
215 + * the block editor in Chromium, is cross-origin to the parent, and a
216 + * Revisions window that restores into the editor still has to hand off.
217 + *
218 + * The parent acts on it only for a window with a submit waiting, or
219 + * one whose frame it cannot read.
220 + *
221 + * A document can also change its address without navigating, and the
222 + * block editor does on its first save: `post-new.php` becomes
223 + * `post.php?post=N&action=edit` through `history.replaceState()`. For
224 + * a frame the parent cannot read, nothing else tells it, so the window
225 + * would go on calling the draft "a new post", and "Copy link" would
226 + * hand out an empty editor. `os-iframe-location` reports every such
227 + * change, and only updates where the parent thinks the frame is: it is
228 + * not a navigation, so it settles no submit and resets no title. The
229 + * wrap is installed here, ahead of every other script, so Core's own
230 + * canonical-URL `replaceState()` in this same head is reported too.
214 231 */
215 232 function openstation_chromeless_navigation_ping_script() {
216 233 if ( ! openstation_is_chromeless_request() ) {
217 234 return;
@@ -217,9 +234,20 @@
217 234 return;
218 235 }
219 236
220 237 wp_print_inline_script_tag(
221 - "try{if(window.parent&&window.parent!==window){window.parent.postMessage({type:'os-iframe-navigated'},window.location.origin);}}catch(e){}"
238 + 'try{if(window.parent&&window.parent!==window){'
239 + . "window.parent.postMessage({type:'os-iframe-navigated',url:window.location.href},window.location.origin);"
240 + . 'var osParent=window.parent,osOrigin=window.location.origin,'
241 + . "osMoved=function(){try{osParent.postMessage({type:'os-iframe-location',url:window.location.href},osOrigin);}catch(e){}};"
242 + . "['pushState','replaceState'].forEach(function(name){"
243 + . 'var original=window.history[name];'
244 + . "if(typeof original!=='function'){return;}"
245 + . 'window.history[name]=function(){var result=original.apply(this,arguments);'
246 + . 'osMoved();return result;};'
247 + . '});'
248 + . "window.addEventListener('popstate',osMoved);"
249 + . '}}catch(e){}'
222 250 );
223 251 }
224 252 add_action( 'admin_head', 'openstation_chromeless_navigation_ping_script', 1 );
225 253
@@ -543,4 +571,79 @@
543 571 wp_enqueue_script( 'os-chromeless-bridge' );
544 572 wp_add_inline_script( 'os-chromeless-bridge', $data, 'before' );
545 573 }
546 574 add_action( 'admin_footer', 'openstation_chromeless_bridge_script' );
575 +
576 +/**
577 + * Transient holding the screen a plugin redirected a plugins window to.
578 + *
579 + * @param int $user_id User ID.
580 + * @return string Transient name.
581 + */
582 +function openstation_plugins_handoff_key( $user_id ) {
583 + return 'openstation_plugins_handoff_' . (int) $user_id;
584 +}
585 +
586 +/**
587 + * Keeps a plugins window on the plugins screen when a plugin redirects
588 + * away from it (Elementor sends a fresh install to its onboarding), and
589 + * leaves the destination for that screen to open in a window of its
590 + * own, the way a link to it would. One hop only, so a plugin that
591 + * redirects on every load cannot loop.
592 + *
593 + * @param string $location Redirect URL.
594 + * @return string The redirect URL, or the plugins screen to land on.
595 + */
596 +function openstation_chromeless_hand_off_plugins_redirect( $location ) {
597 + if ( empty( $location ) || 'plugins.php' !== ( $GLOBALS['pagenow'] ?? '' ) ) {
598 + return $location;
599 + }
600 + if ( ! openstation_is_chromeless_request() || ! openstation_is_admin_redirect_target( $location ) ) {
601 + return $location;
602 + }
603 + $destination = WP_Http::make_absolute_url( $location, self_admin_url() );
604 + if ( 'plugins.php' === basename( (string) wp_parse_url( $destination, PHP_URL_PATH ) ) ) {
605 + return $location;
606 + }
607 + $key = openstation_plugins_handoff_key( get_current_user_id() );
608 + if ( false !== get_transient( $key ) ) {
609 + delete_transient( $key );
610 + return $location;
611 + }
612 + set_transient( $key, $destination, MINUTE_IN_SECONDS );
613 +
614 + // A redirect from inside an action request (a plugin's own
615 + // `activated_plugin` hook) must not run that action again.
616 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only asks whether this is an action request.
617 + if ( empty( $_REQUEST['action'] ) && isset( $_SERVER['REQUEST_URI'] ) ) {
618 + return esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) );
619 + }
620 + return self_admin_url( 'plugins.php' );
621 +}
622 +add_filter( 'wp_redirect', 'openstation_chromeless_hand_off_plugins_redirect', 998 );
623 +
624 +/**
625 + * Opens the screen {@see openstation_chromeless_hand_off_plugins_redirect()}
626 + * kept back, posted as the bridge posts a `target="_blank"` admin link.
627 + */
628 +function openstation_chromeless_open_handed_off_redirect() {
629 + if ( 'plugins.php' !== ( $GLOBALS['pagenow'] ?? '' ) || ! openstation_is_chromeless_request() ) {
630 + return;
631 + }
632 + $key = openstation_plugins_handoff_key( get_current_user_id() );
633 + $destination = get_transient( $key );
634 + if ( ! is_string( $destination ) || '' === $destination ) {
635 + return;
636 + }
637 + delete_transient( $key );
638 +
639 + $message = wp_json_encode(
640 + array(
641 + 'type' => 'os-iframe-admin-link',
642 + 'url' => $destination,
643 + 'label' => '',
644 + 'newContext' => true,
645 + )
646 + );
647 + wp_print_inline_script_tag( 'try{window.parent.postMessage(' . $message . ',window.location.origin);}catch(e){}' );
648 +}
649 +add_action( 'admin_footer', 'openstation_chromeless_open_handed_off_redirect' );