` tags. * * Reported to Elegant Themes. Remove this file when Divi ships * the fix upstream. * * @since 0.8.6 * * @return void */ function desktop_mode_compat_divi_fix_gutenberg_deps() { global $wp_scripts; if ( ! ( $wp_scripts instanceof WP_Scripts ) ) { return; } if ( ! isset( $wp_scripts->registered['et-builder-gutenberg'] ) ) { return; } $registration = $wp_scripts->registered['et-builder-gutenberg']; $existing = (array) $registration->deps; foreach ( array( 'wp-data', 'wp-editor' ) as $dep ) { if ( ! in_array( $dep, $existing, true ) ) { $registration->deps[] = $dep; } } if ( desktop_mode_is_chromeless_request() ) { wp_add_inline_script( 'et-builder-gutenberg', 'window.et_gb = window;', 'before' ); } } add_action( 'enqueue_block_editor_assets', 'desktop_mode_compat_divi_fix_gutenberg_deps', 999 ); /** * Signal Divi's Visual Builder frame-helpers that the iframe context * is "top-level-equivalent" so its `top_window` export resolves to * the iframe's own `window` instead of the desktop shell. * * The VB front-end bundle includes a helper module * (`frontend-builder/build/frame-helpers.js`) whose `top_window` * resolver does roughly this at load time: * * try { u = !!window.top.document && window.top; } * catch ( _ ) { u = false; } * if ( u && u.__Cypress__ ) { * top_window = ( window.parent === u ) ? window : window.parent; * is_iframe = ( window.parent !== u ); * } else if ( u ) { * top_window = u; // ← falls through to here * is_iframe = ( u !== window.self ); * } * * Inside a chromeless iframe `window.top` is the desktop shell, so * the `else if ( u )` branch fires: `top_window = window.top` (the * shell), `is_iframe = true`. The rest of Divi's VB then routes * REST nonces, builder state, and DOM ops through a window that * has none of those things — VB sits permanently on its * "et-fb-page-preloading" loader because the state it's waiting * for will never arrive. * * Setting `__Cypress__` on `window.top` (our shell) makes Divi take * the Cypress branch instead. Since we're a single-level iframe * (`window.parent === window.top`), that branch resolves * `top_window = window` (the iframe itself), `is_iframe = false` * — exactly the classic-admin behavior. The flag costs nothing * outside Divi (no other code in the WP stack reads `__Cypress__`) * and is idempotent (we OR with the existing value). * * Scope: only when the current user has desktop mode enabled AND * the rendered document is loaded inside an iframe (`window.top !== * window`). The inline script is a few-byte no-op everywhere else. * Front-end only — admin pages use `et_gb` (see above) and route * through a different compat path. * * Reported to Elegant Themes. Remove this hook when Divi makes * `top_window` iframe-aware upstream. * * @since 0.8.6 * * @return void */ function desktop_mode_compat_divi_vb_iframe_signal() { if ( is_admin() ) { return; } if ( ! desktop_mode_is_enabled() ) { return; } // Bail when Divi isn't active — the inline script below is // shaped entirely around Divi's frame-helpers and VB preloader. // Other handlers in this file already gate on // `desktop_mode_compat_divi_is_active()`; this one was missed. if ( ! desktop_mode_compat_divi_is_active() ) { return; } // `app_window=1` flags the inner VB iframe Divi spawns inside the // `/?p=N&et_fb=1` page. The outer ("VB-top") frame is what hosts // the visible preloader the user sees; the inner is where Divi // mounts its React app. We only need the preloader bridge on the // VB-top — the inner frame's `__Cypress__` signal is enough. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only flag set by Divi itself when constructing the inner iframe. $is_app_frame = isset( $_GET['app_window'] ) && '1' === sanitize_text_field( wp_unslash( $_GET['app_window'] ) ); ?> chromeless iframe * -> Divi's inner app-frame). Earlier attempts to transparently * eject mid-navigation hit a chain of subtle race conditions — * Divi captures `Location.prototype` references early, makes its * REST save through a path our `fetch`/`XHR` wraps don't reach, * and the page-leave tears down our console before any diagnostic * we add survives the navigation. The honest fix is to ask the * user, explicitly, whether they want to leave Desktop Mode for * this edit session. * * Detection is by visible text content on the clicked element * rather than by selector — Divi changes the button class across * versions but the user-facing label has been stable for years. * We match: "Use Divi Builder", "Use The Divi Builder", "Edit * With The Divi Builder", "Edit With Divi" (case-insensitive, * trimmed). The "Use Default Editor" sibling button is not in the * match set, so users can still keep editing in Gutenberg. * * Scope: chromeless requests only, and only when Divi is active. * The handler also walks every same-origin nested iframe so the * Gutenberg editor canvas (when Gutenberg keeps it for non-Divi * blocks) is covered. * * @since 0.8.6 * * @return void */ function desktop_mode_compat_divi_eject_iframe_patch() { if ( ! desktop_mode_is_chromeless_request() ) { return; } if ( ! desktop_mode_compat_divi_is_active() ) { return; } ?> get( 'Name' ); $template = (string) $theme->get_template(); if ( 'Divi' === $name || 'Divi' === $template ) { return true; } } if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'divi-builder/divi-builder.php' ) ) { return true; } return false; }