| @@ -116,8 +116,10 @@ | ||
| 116 | 116 | |
| 117 | 117 | // Send MIME Type header like WP admin-header. |
| 118 | 118 | @header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) ); |
| 119 | 119 | |
| 120 | + self::send_document_isolation_policy_header(); | |
| 121 | + | |
| 120 | 122 | add_filter( 'show_admin_bar', '__return_false' ); |
| 121 | 123 | |
| 122 | 124 | // Remove all WordPress actions |
| 123 | 125 | remove_all_actions( 'wp_head' ); |
| @@ -539,8 +541,66 @@ | ||
| 539 | 541 | add_filter( 'wp_link_query_args', [ $this, 'filter_wp_link_query_args' ] ); |
| 540 | 542 | add_filter( 'wp_link_query', [ $this, 'filter_wp_link_query' ] ); |
| 541 | 543 | |
| 542 | 544 | add_filter( 'replace_editor', [ $this, 'filter_replace_editor' ], 10, 2 ); |
| 545 | + } | |
| 546 | + | |
| 547 | + /** | |
| 548 | + * Whether the Document-Isolation-Policy header should be sent on the | |
| 549 | + * Elementor editor screen and the editor preview iframe. | |
| 550 | + * | |
| 551 | + * DIP places the document in its own agent cluster, which is the prerequisite | |
| 552 | + * for cross-origin isolation features such as SharedArrayBuffer (required by | |
| 553 | + * WordPress core's client-side media processing introduced in WP 7.1). | |
| 554 | + * | |
| 555 | + * Both the editor parent document and the preview iframe must send the same | |
| 556 | + * DIP header so they join the same agent cluster and synchronous DOM access | |
| 557 | + * between them (e.g. `iframe.contentWindow.elementorFrontend`) keeps working. | |
| 558 | + * | |
| 559 | + * The header is only honored by browsers on a secure context (HTTPS or | |
| 560 | + * localhost) so the helper short-circuits on insecure origins to avoid | |
| 561 | + * sending a header that the browser will ignore. | |
| 562 | + * | |
| 563 | + * @since 4.1.0 | |
| 564 | + * | |
| 565 | + * @return bool | |
| 566 | + */ | |
| 567 | + public static function should_use_document_isolation_policy() { | |
| 568 | + if ( ! is_ssl() ) { | |
| 569 | + $raw_host = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; | |
| 570 | + $host = strtolower( (string) strtok( $raw_host, ':' ) ); | |
| 571 | + | |
| 572 | + if ( 'localhost' !== $host && ! str_ends_with( $host, '.localhost' ) ) { | |
| 573 | + return false; | |
| 574 | + } | |
| 575 | + } | |
| 576 | + | |
| 577 | + /** | |
| 578 | + * Filters whether Elementor sends the Document-Isolation-Policy header | |
| 579 | + * on the editor screen and preview iframe. | |
| 580 | + * | |
| 581 | + * @since 4.1.0 | |
| 582 | + * | |
| 583 | + * @param bool $enabled Whether DIP is enabled. Defaults to true on a secure context. | |
| 584 | + */ | |
| 585 | + return (bool) apply_filters( 'elementor/editor/use_document_isolation_policy', true ); | |
| 586 | + } | |
| 587 | + | |
| 588 | + /** | |
| 589 | + * Send the Document-Isolation-Policy header for the current response. | |
| 590 | + * | |
| 591 | + * Safe to call from both the Elementor editor screen handler and the | |
| 592 | + * preview iframe handler. No-op when {@see self::should_use_document_isolation_policy()} | |
| 593 | + * returns false. | |
| 594 | + * | |
| 595 | + * @since 4.1.0 | |
| 596 | + */ | |
| 597 | + public static function send_document_isolation_policy_header() { | |
| 598 | + if ( ! self::should_use_document_isolation_policy() || headers_sent() ) { | |
| 599 | + return; | |
| 600 | + } | |
| 601 | + | |
| 602 | + header( 'Document-Isolation-Policy: isolate-and-credentialless' ); | |
| 543 | 603 | } |
| 544 | 604 | |
| 545 | 605 | /** |
| 546 | 606 | * Signals to WordPress that Elementor is replacing the block editor on its own editor page, |