` 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;
}